菱形继承(钻石继承)
- 虚拟继承virtual
//菱形继承(钻石继承) wrong example
class Base
{
public:
int base_data;
};
class A : virtual public Base
{
public:
int a_data;
};
class B : virtual public Base
{
public:
int b_data;
};
class D :public A, public B //多继承
{
public:
int d_data;
};
void main()
{
cout<<sizeof(D)<<endl;
D d;
d.a_data = 1;
d.b_data = 2;
d.d_data = 3;
d.A::base_data = 5;
d.B::base_data = 6;
d.base_data = 0;
cout << "sizeof(d) is " << sizeof(d) << endl; //24
cout<<&(d.A::base_data)<<endl;
cout<<&(d.B::base_data)<<endl;
}
以上代码是无法编译过的,但是当屏蔽掉: d.base_data = 0;这一行代码之后,程序就能正常执行了,调试后发现,派生类对象d去访问基类的基类公有数据成员时,就会产生二义性问题,派生类D的直接基类时A和B,A和B都公有继承了base类,相当于A和B中都各自有一个隐藏的base类,然后D继承A和B,D中便有隐藏的A和D,而A和D都有一个隐藏的base类,于是D中就有两个base,通过D去访问base的数据成员便会产生二义性问题
.d的大小时20个字节,d的数据成员d_data占4,其中隐藏的A和B对象各占8,其中4是数据成员,其中4是vbptr( virtual base-table pointer )
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
//菱形继承(钻石继承) right example
class Base
{
public:
int base_data;
};
class A : virtual public Base
{
public:
int a_data;
};
class B : virtual public Base
{
public:
int b_data;
};
class D :public A, public B //多继承
{
public:
int d_data;
};
void main()
{
cout<<sizeof(D)<<endl;
D d;
d.a_data = 1;
d.b_data = 2;
d.d_data = 3;
d.A::base_data = 5;
d.B::base_data = 6;
d.base_data = 0;
cout << "sizeof(d) is "<< sizeof(d) << endl; //24
cout<<&(d.A::base_data)<<endl;
cout<<&(d.B::base_data)<<endl;
}
数据实际还是两份的,只不过是,在整个d对象内存的后面增加了一个基类Base的数据成员,然后通过虚基表虚拟映射过去的,造成无论通过A还是B去访问base_data都是一个值的假象,如下图: