C++继承的问题 : 菱形继承 (虚拟继承)

上一次我们学习了继承相关的知识, 这次我们来看看继承的问题 — 菱形继承.

文章目录

  • 一. 什么是菱形继承
  • 二. 菱形继承的问题
  • 三. 虚拟继承

一. 什么是菱形继承

一图流解释
C++继承的问题 : 菱形继承 (虚拟继承)_第1张图片

二. 菱形继承的问题

就拿上面的经典菱形继承来说, 从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在D类的对象中A类成员会有两份。

.

下面给出菱形继承的代码 :

//菱形继承
class AA {
     
public:
	int _aa;
};

class BB : public AA {
        //AA::_aa
public:
	int _bb;
};

class CC : public AA {
        //AA::_aa
public:
	int _cc;
};

class DD : public BB, public CC {
        //BB::_a   CC::_a
public:
	int _dd;
};

我们可以通过调试看一下DD类对象的数据模型
内存按照继承顺序排列 : 先存放继承的父类(按顺序), 然后存放自己的成员

C++继承的问题 : 菱形继承 (虚拟继承)_第2张图片
可以通过一下代码给d对象赋值

	d.BB::_aa = 1;
	d._bb = 2;
	d.CC::_aa = 5;
	d._cc = 3;
	d._dd = 4;

菱形继承: 公共的基类成员会存在两份, 数据冗余, 且使用时要加作用域, 比较麻烦

C++继承的问题 : 菱形继承 (虚拟继承)_第3张图片
通过显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决

下面是d对象的内存窗口, 一行显示4字节, 可以看到我写的成员变量顺序就是他在内存中排布的顺序
C++继承的问题 : 菱形继承 (虚拟继承)_第4张图片

三. 虚拟继承

虚拟继承解决数据冗余和二义性的原理

拿上面的例子来说, 定义方式是B, C继承A的时候, 在继承方式前面加上virtual关键字

代码如下 :

//菱形继承
class AA {
     
public:
	int _aa;
};

class BB : virtual public AA {
       
public:
	int _bb;
};

class CC : virtual public AA {
       
public:
	int _cc;
};

class DD : public BB, public CC {
      
public:
	int _dd;
};

为了研究虚拟继承如何解决数据冗余的问题, 我们来看看他的内存分布

下面是虚拟继承下的d对象内存分布情况 :

C++继承的问题 : 菱形继承 (虚拟继承)_第5张图片

我们可以看到, 本来BB::_aa 和 CC::_aa 位置的数据变了, 这个数据没有什么规律, 这其实是两个指针, 叫做虚基表指针, 指向两个虚基表, 虚基表中存放着偏移量, 可以通过这个偏移量找到数据_aa

我的机器是小端字节序, 低地址存数据的低位
那么这两个地址转换过来就是 0x00feec280x00feed00
C++继承的问题 : 菱形继承 (虚拟继承)_第6张图片

C++继承的问题 : 菱形继承 (虚拟继承)_第7张图片

通过内存窗口的变化, 我们可以看到, 现在数据_aa存在下面的标红地址处
C++继承的问题 : 菱形继承 (虚拟继承)_第8张图片

这么一看, 上面的偏移量 + 地址 正好就是FD74这个内存地址

这样就解决了数据冗余的问题, 使得_aa只存在一份, 下面我们运行代码看一波

	d.BB::_aa = 1;
	d._bb = 2;
	d.CC::_aa = 5;
	d._cc = 3;
	d._dd = 4;

	d._aa = 8;

C++继承的问题 : 菱形继承 (虚拟继承)_第9张图片

C++继承的问题 : 菱形继承 (虚拟继承)_第10张图片

C++继承的问题 : 菱形继承 (虚拟继承)_第11张图片
可以看到, 用了虚拟继承之后, BB::_aa 和CC::_aa, 与直接用_aa改变的是同一块内存空间, 说明_aa只有一份

OK, 菱形继承的问题就说到这

你可能感兴趣的:(C++语法/实现/相关)