带你领悟菱形继承与虚基表

一、什么是菱形继承

是一种多重继承的特殊情况,一图即懂 :
 

代码表达:

class A {};
class B : public A {};
class C : public A {};
class D : public B ,public C{};

其中 A 作为整个菱形继承 的 最先被继承的类 ,称之为 虚基类

二、菱形继承二义性问题

1.数据冗余

        当 最下层 派生类 (D) 生成实例对象的 时候 ,其内部 空间 中 包含B 的对象空间,也包含C的对象空间 ,若想通过 B 或 C 访问 A 的 成员,就会引发访问不明确的问题(数据冗余所导致的),因为 B 中有一份A 的数据,C 中也有一份 A 的数据 。

代码解释:

class A { public: int _a = 10; };
class B : public A {};
class C : public A {};
class D : public B, public C {};
int main()
{
	D d;
	d._a;    // 此处编译不通过 报访问不明确

	return 0;
}

2.解决方案

使用virtual 关键字 实现 虚继承类 即 B 和 C 进行虚继承 A , 效果是 B C 共同 维护同一个 A 空间

代码解释:

class A { public: int _a = 10; };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public  C {};
int main()
{
	D d;	
	d._a;    // 此时便可以编译通过

	return 0;
}

此时 d 访问的 _a 数据 全局只有一份 ,便可以通过编译。

三、深入了解底层解决原理(虚基表的应用)

1.虚基表

        当B C 分别虚继承类 A 时 ,底层是如何 维护 一个公共空间 A 呢,答案就是 通过虚基表。

(1) 生成时机

        在编译期,如果一个类 有 virtual 修饰的 虚继承类,则 会 生成一个对应的虚基表。

(2) 存储区域

        生成的虚基表作为后面 访问 公共空间的依据,存放在 静态区。

(3) 内容

B C 是 A 的派生类,所以存储的是B C 对应空间地址到 A 空间地址的距离 

(4) 作用流程分析与验证

首先要了解 d 内部的空间占用情况:

当  最底层派生类 D d 对象实例化,其内部空间 会包含三部分 : (c++中是先继承的先构造)

按地址由高到低

B 空间: 会 先包含一个 vbptr 指针 用于 访问虚基表,+ B空间的内容

C 空间:同样也会包含一个vbptr 指针 也用于访问虚基表,+ C 空间的内容

D 自身的数据

当访问 A 中空间数据时,首先会先 在 B C 空间中随机选择一个 作为入口(这里假设选B),

先得到 B中的 vbptr 然后访问对应 虚基表 ,然后通过虚基表找到 B 空间 和 A 空间的 距离(地址偏移量),然后 同一个 B 空间地址 + 对应偏移量 =  A 空间地址 ,进而找到对应的数据

实例:

class A { public: int _a = 4; };
class B : public virtual A { int _b = 1; };
class C : public virtual A { int _c = 2; };
class D : public B, public  C { int _d = 3; };
int main()
{
	D d;	
	d._a;

	return 0;
}

内存调试图解:

四、菱形继承与虚基表两句话总结

菱形继承用 virtual 虚继承把顶层虚基类变成“唯一共享实例”,底层派生类只面对一块内存,消除歧义。
底层对象通过中层类对象空间里的 vbptr 查全局“虚基表”获得偏移,算出共享基类地址再访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值