上图B和C都继承了A,B和C的数据中除了有自己的成员变量外,还有继承自A的成员:_a,此时D同时继承了B和C,那么D中除了自己有的成员变量以外,还包含了继承自B和C的成员变量,此时D中就同时有_b,_c,_d,_a 那么_a究竟是B中的还是C中的呢,而且多继承使得数据大量的重复堆积,使得派生类数据量庞大,这就是菱形继承的数据冗余和二义性的问题
C++中提供了一种解决菱形继承带来的数据冗余和二义性的方法,虚继承
在继承方式前加上关键字:virtual就实现了虚继承
class A
{
public:
int _a;
};
// class B : public A
class B : virtual public A
{
public:
int _b;
};
// class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
从VS的监视窗口可以看出,使用d.B::_a和使用d.C::_a访问的是同一个_a,引入了虚继承确实解决了数据冗余和二义性的问题
实现的原理是虚基表(注意和虚函数表的区分,两者不是一个东西,后者是多态的实现原理)
从内存中可以看出,d的成员变量的地址中除了存有自己的数据以外,还存有一个指针,这个指针就是虚基表指针,它指向一个虚基表,我们可以通过内存查看这个虚基表中的内容,发现它存储了一个值,这个值其实是从当前虚基表指针位置到_a的偏移量(图中显示的数据是16进制表示的,图中显示14说明偏移量是20个字节)
从_b找_a : 0x010FF6F0 + 20 = 0x010FF704 = _a的地址
从_c找_a : 0x010FF6F8 + 12 = 0x010FF704 = _a的地址