使用多重继承是一个好概念还是我可以做其他事情?
多重继承(缩写为 MI)有 味道 ,这意味着 通常 ,它是出于不好的原因而完成的,并且会在维护者面前反击。
这对于继承来说是正确的,因此对于多重继承来说更是如此。
您的对象真的需要从另一个对象继承吗?ACar不需要从 a 继承Engine来工作,也不需要从 a继承Wheel。ACar有一个Engine和四个Wheel。
Car
Engine
Wheel
如果您使用多重继承而不是组合来解决这些问题,那么您做错了。
通常,你有一个类A,然后B和C两者都继承自A. 并且(不要问我为什么)然后有人决定必须从和D继承。B``C
A
B
C
D
B``C
八年来我遇到过两次这样的问题,这很有趣,因为:
A::field
B::field
C::field
如果这不是你想要的,在 C++ 中使用关键字 virtual 来限定继承可以避免上述双重布局,但无论如何,根据我的经验,你可能做错了什么......
在对象层次结构中,您应该尝试将层次结构保持为树(节点有一个父节点),而不是图。
C++ 中的 Diamond of Dread 的真正问题( 假设设计是合理的 - 审查您的代码! ),是 您需要做出选择 :
这种选择是问题所固有的,并且在 C++ 中,与其他语言不同,您实际上可以做到这一点,而无需教条强制您在语言级别进行设计。
但与所有权力一样,这种权力伴随着责任:审查你的设计。
零个或一个具体类的多重继承,以及零个或多个接口通常是可以的,因为你不会遇到上面描述的恐惧钻石。事实上,这就是 Java 中的工作方式。
通常,当 C 继承自A并且B用户可以C像A使用B.
在 C++ 中,接口是一个抽象类,它具有:
零到一个真实对象以及零个或多个接口的多重继承不被认为是“臭”(至少,不是那么多)。
首先,NVI 模式可用于生成接口,因为 真正的标准是没有状态 (即没有成员变量,除了this)。您的抽象接口的重点是发布合同(“您可以这样称呼我,这样称呼我”),仅此而已。只有抽象虚拟方法的限制应该是一种设计选择,而不是一种义务。
this
其次,在 C++ 中,从抽象接口虚拟继承是有意义的(即使有额外的成本/间接)。如果你不这样做,并且接口继承在你的层次结构中出现多次,那么你就会有歧义。
第三,面向对象很棒,但它不是C 中 的 The Only Truth Out There TM。使用正确的工具,并永远记住您在 C 中还有其他范式提供不同类型的解决方案。
有时候是的。
通常,您的C类继承自Aand B,AandB是两个不相关的对象(即不在同一层次结构中,没有共同点,不同的概念等)。
例如,您可以拥有一个Nodes具有 X、Y、Z 坐标的系统,能够进行大量几何计算(可能是一个点,几何对象的一部分),并且每个节点都是一个自动代理,能够与其他代理进行通信。
Nodes
也许您已经可以访问两个库,每个库都有自己的名称空间(使用名称空间的另一个原因......但是您使用名称空间,不是吗?),一个是geo,另一个是ai
geo
ai
所以你有你自己的own::Node派生自ai::Agent和geo::Point。
own::Node
ai::Agent
geo::Point
这是您应该问自己是否不应该改用构图的时候。如果own::Node真的真的是 aai::Agent和 a geo::Point,那么组合就不行了。
然后你需要多重继承,让你own::Node根据他们在 3D 空间中的位置与其他代理进行通信。
(您会注意到ai::Agent并且geo::Point完全、完全、完全不相关......这大大降低了多重继承的危险)
还有其他情况:
有时你可以使用组合,有时 MI 更好。关键是:你有选择。负责任地做(并审查您的代码)。
大多数时候,根据我的经验,不会。MI 不是正确的工具,即使它看起来很有效,因为它可以被懒惰的人用来在没有意识到后果的情况下将功能堆在一起(比如Car同时制作 anEngine和 a Wheel)。
但有时,是的。在那个时候,没有什么比 MI 更有效了。
但是因为 MI 很臭,准备好在代码审查中捍卫你的架构(捍卫它是一件好事,因为如果你不能捍卫它,那么你不应该这样做)。