菱形继承是多重继承中的特殊形式,类中的继承关系呈现出一个菱形。
例如:
下面是一段比较简洁的代码,A、B、C、D类具有菱形继承关系
class A
{
public:int _a;
};
class B : public A
{
public:int _b;
};
class C : public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
现在创建一个D类型的对象,并对它进行相关初始化
D d;d._a = 1;d._a = 2;d._b = 3;d._c = 4;d._d = 5;
这段代码会出现报错,因为B类型继承了一个_a, 而C类型也继承了一个_a, 那么D类型的对象d中有两个_a, 存在数据二义性。报错信息如下:
数据的二义性很好解决,指明作用域就可以
D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;
监视窗口情况:
虽然二义性解决了,但是数据冗余并没有解决。以下是它的内存窗口:
菱形继承存在的两个问题在于:
对于菱形继承存在的问题,解决办法:采用虚继承。
虚继承的做法就是在腰部的位置使用virtual
关键字,将其修饰为虚继承关系
代码修改
class A
{
public:int _a;
};
class B : virtual public A
{
public:int _b;
};
class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
监视窗口的一个变化:
菱形继承的“下顶”类型的相当于直接继承了“上顶”。
经过这样的调试过程来看,它们共同使用一个_a.
第一点发现:数据在内存的顺序,是按照继承顺序的。
第二点发现:采用虚继承类的部分,先是存了一个不知道有什么用的数据,例如dc 7b 15 00
和e4 7b 15 00
然后才保存虚继承类的成员变量。虚继承下来的基类成员变量被保存在了对象中内存的最后,
经过许多调试的经历来看,不难看出dc 7b 15 00
和e4 7b 15 00
是16进制显示的内存地址。分别是地址0X00157BDC
和地址0X00157BE4
,这两个地址有什么用?我们不妨在内存窗口查看一下。
事实上,菱形虚拟继承,内存中只在对象组成的最高处地址保存了一份A,A是B、C公共的。而B和C里分别保存了一个指针,该指针指向一张表。这张表称为虚基表,而指向虚基表的指针称虚基指针。虚基表中保存的值,是到A地址的偏移量,通过这个偏移量就能够找到A了。
下一篇: 《道德与法律究竟是一种什么关系》阅读答案