540-分析并解决C++菱形继承问题

C++多重继承 - 菱形继承问题

B和C的基类是A,B和C都从A单继承而来,D是从B和C多继承而来,D有2个基类:B和C
我们把A称为D的间接的基类
在这里插入图片描述
假设在A里面有ma这个成员变量
在这里插入图片描述
很明显,我们可以看到问题,这里的派生类对象D有间接基类A多份的数据,这在我们的软件设计上肯定是有问题的,假设ma表示名字,我们不可能在派生类D上有2个名字,或者说ma代表身份id,在派生类D不可能有2个身份id,这是设计上的问题。
不仅仅有菱形继承,还有这种继承:
B从A单一继承而来,C有一个基类B,但是还同时从A继承而来。
在这里插入图片描述
假设ma是类A的一个属性。
在这里插入图片描述
很明显,出现了同样的问题。
C不可能有2个相同意义的属性。

多重继承的弊端:派生类有多份间接基类的数据 设计的问题
好处是可以做更多代码的复用

在设计软件项目,如果可以,我们要尽量避免多重继承的情况,避免出现以上问题。

菱形继承的案例

class A
{
public:
	A(int data) :ma(data) { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
protected:
	int ma;
};
//=======================================
class B : public A
{
public:
	B(int data) :A(data), mb(data) { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
protected:
	int mb;
};
class C : public A
{
public:
	C(int data) :A(data), mc(data) { cout << "C()" << endl; }
	~C() { cout << "~C()" << endl; }
protected:
	int mc;
};
//=========================================
class D : public B, public C
{
public:
	D(int data) : B(data), C(data), md(data) { cout << "D()" << endl; }
	~D() { cout << "~D()" << endl; }
protected:
	int md;
};
int main()
{
	D d(10);

	return 0;
}

d对象内存的布局:

在这里插入图片描述
两个ma分别在B和C的构造函数里面进行
在这里插入图片描述
D调用了2次A的构造
不管是内存占用上,还是数据的重复,我们的软件设计是要杜绝的。

菱形问题怎么处理呢

虚继承。
所有从A继承的地方,都采用virtual虚继承,

在这里插入图片描述
这样,A就成了虚基类了。
B从A虚继承,A成为虚基类,就要搬到派生类的最后面。
在这里插入图片描述
C也从A虚继承而来。也要把ma搬到最后面。搬的时候,发现最后面已经有一份虚基类的数据了,那么当前这份虚基类数据就不要了,同样的放一个vbptr
在这里插入图片描述
现在在派生类D里面,现在只有1份基类的数据。
在这里插入图片描述
不管访问B下的ma,还是访问C下的ma,都是同一个ma

B和C都要采用虚继承哦。
如果其中一个没有采用虚继承,ma还是会重复。

在这里插入图片描述
也就是说,所有从A继承的地方都要使用虚继承。
最终,在派生类的最后面只会放1份虚基类的数据。
但是,我们把虚基类的数据放到了派生类的最后面来。
也就是说,D给B,C说:你们不要每个人都拿1份ma了,这个ma直接给我,我用1份,你们要访问,都跑到我这个ma来,这样一来,ma的初始化从作用域上来说,已经靠近D了,是属于D上的,不需要B或者C来负责初始化了,ma这个虚基类数据需要由D自己来初始化。

class A
{
public:
	A(int data) :ma(data) { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
protected:
	int ma;
};
//=======================================
class B : virtual public A
{
public:
	B(int data) :A(data), mb(data) { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
protected:
	int mb;
};
class C : virtual public A
{
public:
	C(int data) :A(data), mc(data) { cout << "C()" << endl; }
	~C() { cout << "~C()" << endl; }
protected:
	int mc;
};
//=========================================
class D : public B, public C
{
public:
	//“A::A”: 没有合适的默认构造函数可用
	D(int data) :A(data), B(data), C(data), md(data) { cout << "D()" << endl; }
	~D() { cout << "~D()" << endl; }
protected:
	int md;
};
int main()
{
	D d(10);

	return 0;
}

在这里插入图片描述
菱形继承问题解决了!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++菱形继承是指一个派生同时继承了两个直接或间接基类,而这两个基类又间接或直接继承自同一个基类,从而形成了一个菱形的继承关系。 例如下面的代码: ``` 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; }; ``` 在这个例子中, `D` 继承 `B` 和 `C`,而 `B` 和 `C` 都继承 `A`,因此形成了一个菱形继承关系。 菱形继承会引起一些问题,例如: 1. 内存浪费:由于 `A` 被重复继承,导致在内存中存在两份相同的 `A` 对象,造成内存浪费。 2. 访问冲突:由于 `D` 继承 `B` 和 `C`,而这两个继承 `A`,因此在 `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; }; ``` 在这个例子中, `B` 和 `C` 继承 `A` 时使用了 `virtual` 关键字,表示使用虚继承。这样, `D` 中就只有一个 `A` 对象的实例,而且访问 `A` 中的成员也不会出现访问冲突的问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值