1.菱形继承(钻石继承):两个子类继承同一父类,而又有子类同时继承这两个子类。例如B,C两个类同时继承A,但是又有一个D类同时继承B,C类。

2.菱形继承的对象模型

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;
};

上述的代码对应的菱形继承的对象模型如下图所示

3.菱形继承所带来的问题
(1)由上图的对象模型可以看出,D的对象中有两份A成员,造成了数据的冗余。使用sizeof去验证,可以发现d的大小为20个字节。

(2)会造成基类子对象重复,即二义性。造成访问基类的成员不明确。

4.解决菱形继承所带来的问题
(1)要解决二义性很简单,可以显示的指定访问哪个父类的成员,但是这个还是不能从根本上解决这个问题。如:

D d;
d.B::_a;
//d.C::_a;
//上述两种方法取其一就可以规避这个问题

(2)要从本质上解决上述问题,需要采用虚继承:虚继承是一种机制,类通过虚继承指出它希望共享虚基类的状态。对给定的虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象,共享基类子对象称为虚基类。虚基类用virtual声明继承关系就行了。这样一来,D就只有A的一份拷贝。
如:

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;
};

采用虚继承和显示指定访问父类成员,对对象所产生的的影响有什么不同呢?显示指定访问并不能起到一改全改的作用,那么就会造成一个对象d有两个不相同的成员_a,明显是不符合常规的。

 1. 显然我们不用显示的指定访问哪个父类的成员,但是可以清晰看到d的大小并不是期望中的16,而是24个字节,这是为什么呢?


5.深入理解虚继承
现在来了解多出来的四个字节究竟是什么?首先我们通过调试——>内存查看对象d的地址的数据,可以看到如下现象:


(1)(2)中存放的数据究竟是用来表示什么的呢?再将d._a分别赋值成为0和4,通过内存窗口观察d的地址的数据

由图可以看到末排的地址存放的数据刚好是d._a的值的大小,那么多出来的第一排和第三排的数据看起来似乎是两个地址,输入地址查看一下数据

明显可以看到两个地址存放的数据是每个对象相对于基类的成员的偏移量,对应的每个地址所代表的意义如下:

这样也就可以解释为什么d的大小为24个字节因为虚继承引入了间接性指针
6.在虚继承的前提下,重新建立对象模型

7.在菱形继承且为虚继承的前提下,讨论虚基表为什么首先不存偏移量,而是在存偏移量之前预留了一个0x0000 0000的位置呢?
(1)首先,设置一个菱形继承,且为虚继承,每个子类既重写了父类的虚函数,还拥有自己的虚函数

class A
{
public:virtual void f1(){cout << "A::f1()" << endl;cout << endl;}virtual void f2(){cout << "A::f2()" << endl;cout << endl;}int _a;
};class B :virtual public A
{
public:virtual void f1(){cout << "B::f1()" << endl;cout << endl;}virtual void f3(){cout << "B::f3()" << endl;cout << endl;}int _b;
};
class C :virtual public A
{
public:virtual void f1(){cout << "C::f1()" << endl;cout << endl;}virtual void f4(){cout << "C::f4()" << endl;cout << endl;}int _c;
};class D :public B,public C
{
public:virtual void f1(){cout << "D::f1()" << endl;cout << endl;}virtual void f5(){cout << "D::f5()" << endl;cout << endl;}int _d;
};

用菱形继承的模型图来表示关系就是:

通过实例化出对象d,并查看内存可以看到对象布局,可以看到有五个类似地址的部分,一一通过内存窗口查看内容,发现三张为虚表(存放虚函数),两张为虚基表(存放偏移量的),在下图中用不同的颜色标注出来了:

通过内存窗口查看各个虚基表的地址:

可以看到虚基表的开始预留一个位置是用来存放虚基表和虚表的地址之差的。
内存分布模型:

那么可以看到实例化出来的对象d的对象模型为:

至于虚表存放的虚函数顺序是怎么存放的:
http://blog.csdn.net/skyroben/article/details/68192874

菱形继承和虚继承、对象模型和虚基表相关推荐

  1. C++ 面向对象(一)继承:继承、对象切割、菱形继承、虚继承、继承与组合

    目录 继承 继承的概念 继承方式 基类与派生类的赋值转换 作用域与隐藏 派生类的默认成员函数 友元与静态成员 友元 静态成员 多继承 菱形继承 虚继承 继承和组合 什么是组合 如何选择组合和继承 继承 ...

  2. C++继承详解三 ----菱形继承、虚继承

    转载:http://blog.csdn.net/pg_dog/article/details/70175488 今天呢,我们来讲讲菱形继承与虚继承.这两者的讲解是分不开的,要想深入了解菱形继承,你是绕 ...

  3. C++ 继承 | 对象切割、菱形继承、虚继承、对象组合

    文章目录 继承 继承的概念 继承方式及权限 using改变成员的访问权限 基类与派生类的赋值转换 回避虚函数机制 派生类的默认成员函数 友元与静态成员 多继承 菱形继承 虚继承 组合 继承 继承的概念 ...

  4. c/c++入门教程 - 2.4.6 继承、公共继承、保护继承、私有继承、virtual虚继承(概念、语法、方式、构造和析构顺序、同名成员处理、继承同名静态成员处理、多继承语法、菱形继承、钻石继承)

    目录 4.6 继承 4.6.1 继承的基本语法 4.6.2 继承方式 4.6.3 继承中的对象模型 4.6.4 继承中构造和析构顺序 4.6.5 继承同名成员处理方式 4.6.6 继承同名静态成员处理 ...

  5. C++对象模型:单继承,多继承,虚继承

    什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 cla ...

  6. C++之菱形继承与虚继承(含虚函数)

    面向对象的三大特征:封装,多态,继承 前面我们已经讲了继承的一些知识点,在这基础上,我们讲的时候再涉猎一些多态的只是. 下面我们先接着上次讲有虚函数的菱形虚继承 首先什么是虚函数.? 虚函数:在类里面 ...

  7. c++对象模型05:虚继承内存布局

    虚继承 虚继承解决了菱形继承中最派生类拥有多个间接父类实例的情况.虚继承的派生类的内存布局与普通继承很多不同,主要体现在: 虚继承的派生类,如果定义了新的虚函数,则编译器为其生成一个虚函数指针(vpt ...

  8. 钻石问题(菱形继承问题) 和虚继承

    在C++中,什么叫做钻石问题(也可以叫菱形继承问题),怎么避免它? 下面的图表可以用来解释钻石问题. 假设我们有类B和类C,它们都继承了相同的类A.另外我们还有类D,类D通过多重继承机制继承了类B和类 ...

  9. C++对象模型6——g++中虚继承的实现

    虚基类的实现法在不同编译器之间差异极大.然而,每一种实现法的共同点在于必须能够在执行期准确描述虚基类在其每一个派生类中的位置. 一.虚继承的实现 struct A {int ax;virtual vo ...

最新文章

  1. 2021年大数据Spark(二十八):SparkSQL案例三电影评分数据分析
  2. SpringBoot中使用 Druid 数据库连接池, 后台SQL监控无效
  3. javase阶段总结脑图
  4. 【双指针】Square Pasture G(P7153)
  5. 求质数算法的N种境界 (N 10) zz
  6. 移动游戏市场Testin云测占有率超过90%
  7. 设计模式——工厂方法
  8. SpringBoot热部署(实战)详解
  9. Unity VideoPlayer视频播放器
  10. 网页添加QQ/MSN链接
  11. 无线承载根据承载的内容不同分为SRB和DRB EPS承载根据用户业务需求和Qos的不同可以分为GBR/ Non-GBR 承载...
  12. maven setting配置超详解
  13. 银行卡号码显示每隔4位数空一格
  14. 尤雨溪:重头来过的 Vue3 带来了什么?
  15. 如何树立正确的工作态度
  16. chi2inv函数 matlab_matlab函数与指令大全 a——h (转载)
  17. Android版计算器(java实现,包含小数、负数、括号)代码和讲解
  18. 如何清理网易云无损flac格式留下的注释163key(Don't Modify):.........等牛皮癣?
  19. 微信小程序低功耗蓝牙
  20. Repeater三级嵌套

热门文章

  1. 硬解析和软解析 mysql_Oracle学习之shared pool--硬解析和软解析
  2. RabbitMQ的5种队列_通配符模式_入门试炼_第8篇
  3. 前后端分离,如何解决跨域(代理模式)、路由拦截(进入页面需要登录)以及请求拦截(登录TOKEN失效)等问题(初学者)
  4. java调用qq接口_用java代码怎么去请求腾讯接口并返回值
  5. C++面向对象思想 两条直线交点计算
  6. 华为 招聘 状态 查看_英特尔放弃5G芯片研发:三星受益;爱立信2019年Q1净利润约2.51亿美元;华为也将要与高通和解;天津首个5G电话成功打通...
  7. php中$stu_by,PHP基础案例二:计算学生年龄
  8. java管理员登录_idea实现管理员登录javaweb
  9. linux下查看cmake的版本
  10. IOS – OpenGL ES 调节图像色彩替换 GPUImageFalseColorFilter