C++的虚继承主要解决了数据冗余与二义性的问题,起实现方法是什么呢,我们先看一段代码。
#include<iostream> using namespace std; class A { public: int _num; }; class B1 : public A { }; class B2 : public A { }; class C : public B1, public B2 { }; int main() { C c; cout << sizeof(c) << endl; c.B1::_num = 1; c.B2::_num = 2; cout << "c.B1::_num:" << c.B1::_num << endl; cout << "c.B2::_num:" << c.B2::_num << endl; cout << "c._num:" << c._num << endl; return 0; }
这是一个菱形继承,直接访问两个域中的_num并赋不同值时,两个域中的_num不会是同一个数值,证明了c中含有两个_num成员变量。当我们访问C域的_num,系统无法识别到底是访问B1所继承下来的还是B2所被继承下来的,这里编译器会报错,这里c的大小为8个字节,调试点开监视窗口我们可以看到:
一个C在不同域里继承了两份A类的成员变量,这样就造成了数据冗余及二义性。
如何解决这个问题呢,C++中引入了关键字“vritual”在一代继承时加入关键字,修改后我们的代码为:
#include<iostream> using namespace std; class A { public: int _num; }; class B1 : virtual public A { }; class B2 : virtual public A { }; class C : public B1, public B2 { }; int main() { C c; cout << sizeof(c) << endl; c.B1::_num = 1; c.B2::_num = 2; cout << "c.B1::_num:" << c.B1::_num << endl; cout << "c.B2::_num:" << c.B2::_num << endl; cout << "c._num:" << c._num << endl; return 0; }
这时我们发现通过c我们不但可以访问到自己域的成员变量,还可以访问到所继承类的成员变量,并且,当给不同域的同名成员变量赋值之后,它们的外在表现都是同一个值,这样节解决了二义性与数据冗杂,为什么说是外在表现呢,当我们打开调试窗口与内存时我们会看到:
调试窗口:
调试窗口中,c不但包含两个父类的成员变量,还包含两个父类共同的父类的成员变量,这时c的大小为12。更惊奇的是,无论我们通过那个域名对其成员变量进行赋值,其他域的成员变量的值也会随之改变,这时为什么呢。这里我们打开内存监视窗口:
这里我们可以看到,c中存的并不是真正的三个类中的成员变量,而是两个指针,与一个成员变量,拿着两个指针究竟指向的是什么呢,我们再打开更多的内存窗口进行观察:
第一个指针:
第二个指针:
我们可以发现,这两个指针都为空,但是,在这两个指针底下,却有两个数字,这两个数字又恰好与存放这两个地址内容所在地址到存放c成员变量所在地址的偏移相同。由此可以看出VS下实现virtual关键字解决菱形继承的二义性与数据冗余的方法是,通过指针的偏移,使c中所存在的三块空间,在直接或间接的条件下,指向同一块空间。
未完待续
本文出自 “pawnsir的IT之路” 博客,请务必保留此出处http://10743407.blog.51cto.com/10733407/1750123
原文地址:http://10743407.blog.51cto.com/10733407/1750123