菱形继承、菱形虚拟继承、以及菱形虚拟继承的模型结构内部。

本文探讨了C++中的菱形继承问题,包括数据冗余和二义性,并详细解释了如何通过虚拟继承来解决这些问题。通过实例代码和对象模型结构,展示了虚拟继承如何消除数据冗余,以及在内存中的表现。最后强调了尽量避免使用菱形继承,推荐使用组合以提高代码的维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 单继承:一个子类只有一个直接父类。

 

多继承:一个子类有两个或以上直接父类。

菱形继承:菱形继承是多继承的一种特殊情况。 

 

下面是代码和对象模型结构,可以看出菱形结构存在哪些问题,如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
using namespace std;

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

那么我要访问 _a,会出现什么?

 

 

因为 B 和 C 都继承了 A 中的 _a,所有访问 _a 的话,不明确访问的是哪个 _a,需要指定访问,如下:

  

下面是对象模型结构图,如下:

菱形继承问题:从上面的代码和对象模型结构中可以看到菱形继承有数据冗余和二义性问题。

D 继承 B、C之后,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;
};

int main()
{

	D d;

	d.B::_a = 1;
	d.C::_a = 2;

	d._b = 3;
	d._c = 4;
	d._d = 5;

	return 0;
}

在 B、C 继承 A 的时候,加上 virtual 就可以了。

下面借助调试窗口看一下菱形继承之后,不加 virtual,那么内部是什么样?

可以看到 d 对象中存在两个 _a,存在数据冗余。

那么虚拟继承之后呢,下面 加virtual 看一下,如下?

上图中可以看到,D 对象中把 _a 放到了最下面,相比于不加virtual的时候,B、C 对象中没有了 _a 但是多了一个地址,这个地址就是去找 _a 的,这个地址就是指向一张表的,这个表叫虚机表,指向虚机表的指针叫虚机表指针,虚机表中存的是偏移量,通过偏移量可以找到_a。

总结:虚继承之后,B、C对象里面没有了 _a,但是多了一个指针,这个指针是找 _a 的,先找到虚机表,在获取里面的偏移量,再拿偏移量找 _a。

下面我打开两个内存窗口调试看一下,如下:

 

 

B 中找偏移量是 20,不难看出地址是 4 字节(32位下),_a 在第 5 个位置;C 与 B 类似。

总结:

1. 能不用菱形继承就不要用!!!!!!!!!

2. 继承是一种 is-a 的关系;组合是一种 has-a 的关系。

3. 优先使用组合,而不是继承。(低耦合)降低耦合度,维护性好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值