类--菱形继承

菱形继承

一、普通继承

1.本质

会继承父类的全部成员。

2.原理

在普通继承方式下,当子类继承父类的全部成员后,可能会出现重复的成员,这些成员可能来自父类的父类。

3.内存结构

对于菱形继承,D继承B和C,B和C均继承A,则D的成员变量如下所示。

  1. B的成员变量
  2. C的成员变量
  3. 自己的成员变量
0x007DF718  01 00 00 00 02 00 00 00  ........
0x007DF720  01 00 00 00 03 00 00 00  ........
0x007DF728  04 00 00 00 cc cc cc cc  ....????

4.注意点

  1. 从不同路径继承来的同一基类,会在子类中存在多份拷贝,从而浪费存储空间;
  2. 将基类指针指向子类中对象的地址,但是多重继承可能存在一个基类的多份拷贝,从而引发二义性;

会使得对象无法确定到底是哪个类的成员变量。

二、虚继承

1.本质

解决普通的菱形继承所产生的二义性和浪费空间的问题。

在继承属性前面加上“ virtual ”关键字。

class B:virtual public A {}

2.原理

在使用虚继承的菱形继承方式下,子类继承两个父类,且两个父类均虚继承父类的父类,则会出现一个指向存储偏移地址的空间的指针,且能够解决二次项和浪费资源的问题。

3.内存结构

对于使用虚继承的菱形继承,D继承B和C,B和C均虚继承A,则D的成员变量如下所示。

  1. B的定位指针和成员变量;
  2. C的定位指针和成员变量;
  3. 自己的成员变量;
  4. A的成员变量;
0x00B5FB9C  48 8b bb 00 02 00 00 00  H??.....
0x00B5FBA4  54 8b bb 00 03 00 00 00  T??.....
0x00B5FBAC  04 00 00 00 01 00 00 00  ........

4.定位指针所指向空间的内存结构

(1)内存结构成员

第一成员——全0

第二成员——父类到 父类的父类 的偏移

0x00BB8B48  00 00 00 00 14 00 00 00  ........
(2)从对象基地址跳转到 父类的父类

需要3行汇编代码解决。

  1. 先取当前对象首地址到 ecx 寄存器;
  2. 将 ecx 寄存器里面的地址+4,取出父类到 父类的父类 的偏移;
  3. 将 基地址+偏移 可以从当前对象定位到父类所属区域;
mov ecx,dword ptr [Object]
mov edx,dword ptr [ecx+4]
lea eax,Object[edx]
(3)代码

从子类定位到 父类的父类 。

A* a = &Object;

三、存在虚函数的虚继承

1.本质

在已经出现虚继承的基础上,父类以及父类的父类均有虚函数。

2.原理

在使用虚继承的菱形继承方式下,子类继承两个父类,且两个父类均虚继承父类的父类,则会出现一个指向存储虚表地址和偏移地址的空间的指针。

3.内存结构

对于使用虚继承的菱形继承,D继承B和C,B和C均虚继承A,则D的成员变量如下所示。

  1. B的虚表地址、定位指针、成员变量;
  2. C的虚表地址、定位指针、成员变量;
  3. 自己的成员变量;
  4. A的虚表指针、成员变量;
0x012FF844  7c 9b 4a 00 9c 9b 4a 00  |?J.??J.
0x012FF84C  02 00 00 00 88 9b 4a 00  ....??J.
0x012FF854  a8 9b 4a 00 03 00 00 00  ??J.....
0x012FF85C  04 00 00 00 94 9b 4a 00  ....??J.
0x012FF864  01 00 00 00 cc cc cc cc  ....????

4.定位指针所指向空间的内存结构

(1)内存结构成员

第一成员——当前地址到本类虚表的偏移(一般为负数)

第二成员——父类到 父类的父类 的偏移

0x004A9B9C  fc ff ff ff 18 00 00 00  ?.......
(2)从对象基地址跳转到 父类的父类

需要3行汇编代码解决。

  1. 先取当前对象首地址到 ecx 寄存器;
  2. 将 ecx 寄存器里面的地址+4,取出父类到 父类的父类 的偏移;
  3. 将 基地址+偏移 可以从当前对象定位到父类所属区域;
mov ecx,dword ptr [Object]
mov edx,dword ptr [ecx+4]
lea eax,Object[edx]

四、构造函数的调用

1.本质

对于菱形继承,无论是否使用虚继承,都不会反复调用 父类的父类 的构造函数。

2.原理

使用汇编代码 push 0 作为调用构造函数的标记。

  1. 首先执行爷爷的构造函数,然后 push 0;
  2. 然后执行父亲的构造函数,然后 push 0;
  3. 之后执行叔叔的构造函数;
  4. 最后执行自己的构造函数;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x85aFbpg-1685698370444)(C:\Users\鹘鹰折翼\AppData\Roaming\Typora\typora-user-images\菱形继承的构造函数.png)]

五、析构函数的调用

1.本质

对于菱形继承,无论是否使用虚继承,都不会反复调用 父类的父类 的析构函数。

2.原理

  1. 首先执行自己的析构函数;
  2. 然后执行叔叔的构造函数;
  3. 之后执行父亲的构造函数;
  4. 最后执行爷爷的构造函数;

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值