我怎样才能避免钻石继承

How could I avoid diamond inheritance?

本文关键字:钻石 继承 能避免      更新时间:2023-10-16

我目前正在研究一个C++设计,其中我有这个继承结构:

  A
 / 
B   C

A执行类BC共同的计算,类BC是初始化A的两种不同方式。

我想添加某种混合初始化,即一个类D,它将使用 BC 的方法。

但是,我需要使用钻石继承才能访问B::init()C::init()来设置D的属性。

我知道我可以使用虚拟继承来避免钻石问题,但是当我手动复制方法时,我遇到了运行时错误。此外,我在尝试实例化类BC时遇到了问题,一位同事建议我永远不要在我的设计中使用钻石继承。

因此,我想找到某种"干净"的解决方法,但我无法做到。

  • 我可以将所有初始化例程放在类A中,但目前它们被很好地分开了,我想避免有一个大类,在那里我无法真正分离类的不同函数组BC。回答后编辑:这就是我选择的,使用不同的 cpp 文件将我的"大"类拆分为逻辑方法组。

  • 我还可以删除继承链接并将它们替换为友谊,其中BC方法是静态的,并且在类型为 A* 的指针上工作。这样,我就可以打电话给B::init(A* a),并从D::init(A* a) C::init(A* a)。但是,我必须用a->_fooAttribute替换_fooAttribute的所有用法,这有点麻烦,似乎不对。

你会推荐什么?

如果您的设计需要钻石继承,那么这就是您需要做的。人们将其视为C++的"不得使用"功能,但事情的真相是它就在那里,它是完全定义的(如果理解起来有点复杂),如果你的问题空间需要它,你应该使用它。

特别是,我无法理解这是否确实是钻石遗产。特别是,B内部的AC内部的AA同一实例是否有意义?从你的问题来看,似乎不是。BC都有某种不同的初始化A方式。如果是这样的话,这不是钻石遗产。只需确保BC在非虚拟继承中继承A即可。

话虽如此,请确保这确实是您的设计所要求的。你能诚实地说B是一个A吗?那C?你能诚实地说D既是B又是C吗?如果没有,也许让A成为BC或两者的成员,或者让BC D的成员更有意义。

如果您从A继承的唯一原因是作为扩展A提供的方法的一种方式,那么请考虑简单地将这些方法作为A的成员。如上所述,虽然减少代码重复是一个有价值的原因,但设计应确保继承关系是一种关系。偏离这一点就是自找麻烦。

继承是一种"是"关系。如果B是A,那么你很好。这同样适用于 C。从你的描述来看,你没有这种关系。相反,您有一个执行计算的实用程序类 (A)。您可能希望使它具有静态方法,因为如果它确实是一个实用程序,则它本身不需要存储任何数据。向 A 传递 B 或 C 的实例并让它使用 B->fooAttribute 访问所需的属性并没有错。但是,您可能希望 B 和 C 都实现一个通用接口,这样您就不必知道您正在查看哪个接口。