当前位置:  开发笔记 > 编程语言 > 正文

为什么我应该避免C++中的多重继承?

如何解决《为什么我应该避免C++中的多重继承?》经验,为你挑选了6个好方法。

使用多重继承是一个好主意还是我可以做其他事情?



1> paercebal..:

多重继承(缩写为MI)闻起来,这意味着通常,它是出于不好的原因而完成的,它会在维护者面前反击.

摘要

    考虑功能的组合,而不是继承

    警惕恐惧钻石

    考虑继承多个接口而不是对象

    有时,多重继承是正确的.如果是,则使用它.

    准备好在代码审查中捍卫您的多重继承架构

也许是作文?

这对于继承来说是正确的,因此,对于多重继承来说更是如此.

你的对象真的需要从另一个继承吗?A Car不需要继承Engine工作,也不需要继承Wheel.A CarEngine四个Wheel.

如果你使用多重继承来解决这些问题而不是组合,那么你做错了.

2.恐惧钻石

通常情况下,你有一个类A,然后BC来自继承A.而且(不要问我为什么)有人然后决定D必须继承BC.

我在8个8年里遇到过这种问题两次,看到因为:

    多少钱一个错误是从一开始(在这两种情况下,D不应该从两个继承BC),因为这是不好的架构(其实,C不应该存在在所有...)

    有多少维护者为此付出了代价,因为在C++中,父类A在其孙子类中出现了两次D,因此,更新一个父字段A::field意味着更新它两次(通过B::fieldC::field),或者有一些东西是默认错误并且崩溃,之后(新的指针B::field,并删除C::field...)

如果这不是你想要的,那么在C++中使用关键字virtual来限定继承可以避免上面描述的双重布局,但无论如何,根据我的经验,你可能做错了......

在对象层次结构中,您应该尝试将层次结构保持为树(节点具有一个父节点),而不是图形.

关于钻石的更多信息(编辑2017-05-03)

C++中的恐惧钻石的真正问题(假设设计合理 - 让你的代码得到审查!),你需要做出选择:

这个类是否适合A在你的布局中存在两次,这是什么意思?如果是,那么一定要从它继承两次.

如果它只存在一次,那么实际上从它继承.

这个选择是问题所固有的,而且在C++中,与其他语言不同,您实际上可以在没有教条的情况下执行此操作,从而在语言级别强制设计.

但是像所有权力一样,拥有这种权力的责任就在于:让您的设计得到审查

3.接口

零或一个具体类的多重继承,以及零个或多个接口通常是好的,因为您不会遇到上述的恐惧钻石.事实上,这就是用Java完成的事情.

通常情况下,你的意思是当C从继承AB是用户可以使用C,如果它是一个A,和/或如果它是一个B.

在C++中,接口是一个抽象类,它具有:

    其所有方法声明为纯虚拟(后缀为= 0) (删除了2017-05-03)

    没有成员变量

零到一个真实对象以及零个或多个接口的多重继承不被认为是"臭"(至少不是那么多).

有关C++抽象接口的更多信息(编辑2017-05-03)

首先,NVI模式可用于生成接口,因为真正的标准是没有状态(即没有成员变量,除了this).你的抽象界面的意思是发布合同("你可以用这种方式打电话给我,这样"),仅此而已.仅具有抽象虚拟方法的限制应该是设计选择,而不是义务.

其次,在C++中,从抽象接口虚拟继承是有意义的(即使有额外的成本/间接).如果不这样做,并且接口继承在层次结构中出现多次,那么您将有歧义.

三,面向对象是伟大的,但它不是唯一的真理在那里TM在C++中.使用正确的工具,并始终记住您在C++中提供其他范例,提供不同类型的解决方案.

你真的需要多重继承吗?

有时候是.

通常情况下,你的C类继承AB,和AB是两个不相关的对象(即不在同一层次,没有任何共同之处,不同的概念,等等).

例如,您可以拥有一个Nodes具有X,Y,Z坐标的系统,能够进行大量的几何计算(可能是一个点,几何对象的一部分),每个节点都是一个自动代理,能够与其他代理进行通信.

也许您已经可以访问两个库,每个库都有自己的命名空间(使用命名空间的另一个原因......但是你使用命名空间,不是吗?),一个正在存在geo,另一个存在ai

所以你有自己的own::Node衍生出来ai::Agentgeo::Point.

这是你应该问自己是否不应该使用构图的那一刻.如果own::Node真的真的是a ai::Agent和a geo::Point,那么组合就不行了.

然后,您将需要多重继承,own::Node根据他们在3D空间中的位置与其他代理进行通信.

(你会注意到ai::Agent并且geo::Point完全,完全,完全不相关......这大大降低了多重继承的危险)

其他情况(编辑2017-05-03)

还有其他情况:

使用(希望是私有的)继承作为实现细节

一些像策略这样的C++习语可以使用多重继承(当每个部分需要通过其他部分进行通信时this)

来自std :: exception的虚拟继承(异常是必需的虚拟继承吗?)

等等

有时你可以使用成分,有时MI更好.关键是:你有一个选择.负责任地做(并检查您的代码).

那么,我应该做多重继承吗?

大部分时间,根据我的经验,没有.MI是不正确的工具,即使它似乎工作,因为它可以由惰性用来堆特征组合在一起而没有意识到后果(如制作Car既有EngineWheel).

但有时候,是的.而在那个时候,没有比MI更好的了.

但是因为MI很臭,所以要准备好在代码审查中捍卫你的架构(并且保护它是一件好事,因为如果你无法保护它,那么你就不应该这样做).


,这意味着
,它是出于不好的原因而完成的,它会在维护者面前反击.
),
在C++中.使用正确的工具,并始终记住您在C++中提供其他范例,提供不同类型的解决方案.

@BillK:当然没有必要.如果你有一个没有MI的转换完整语言,你可以做任何与MI有关的语言.所以是的,没有必要.永远.也就是说,它可以是一个非常有用的工具,也就是所谓的Dreaded Diamond ......我从来没有真正理解为什么"可怕"这个词甚至存在.它实际上很容易推理,通常不是问题.
我认为它并不是必需的,而且很少有用,即使它是一个完美的契合,代表的节省也不会弥补不习惯使用它的程序员的困惑,而是其他好的总结.
我添加到第5点,使用MI的代码旁边应该有一个注释来解释原因,因此您不必在代码审查中口头解释.如果没有这个,那么不是你的评论者的人可能会在他们看到你的代码时质疑你的良好判断,而你可能没有机会为其辩护.

2> Nemanja Trif..:

从对Bjarne Stroustrup的采访中:

人们非常正确地说你不需要多重继承,因为你可以用多重继承做任何事情,你也可以用单继承做.你只需使用我提到的委托技巧.此外,您根本不需要任何继承,因为您使用单继承执行的任何操作也可以通过转发类来进行继承.实际上,您也不需要任何类,因为您可以使用指针和数据结构来完成所有操作.但是你为什么要那样做呢?什么时候使用语言设施方便?你什么时候想要一个解决方法?我已经看到了多继承很有用的情况,我甚至看到过很复杂的多继承很有用的情况.通常,我更喜欢使用该语言提供的功能来进行变通


+1:完美答案:如果您不想使用它,请不要使用它.
我在C#和Java中错过了C++的一些功能,尽管它们会使设计变得更加困难/复杂.例如,`const`的不变性保证 - 我必须编写笨重的变通方法*(通常使用接口和组合)*当一个类真正*需要*具有可变和不可变变量时.但是,我从来没有*曾经*错过多重继承,并且从未觉得由于缺少此功能我必须编写一个解决方法.这就是区别.在我见过的每一个案例中,**不是**使用MI是更好的设计选择,而不是解决方法.

3> 小智..:

没有理由避免它,它在情况下非常有用.但您需要了解潜在的问题.

最大的一个是死亡钻石:

class GrandParent;
class Parent1 : public GrandParent;
class Parent2 : public GrandParent;
class Child : public Parent1, public Parent2;

您现在在Child中有两个GrandParent"副本".

C++已经想到了这一点,并允许你做虚拟继承来解决问题.

class GrandParent;
class Parent1 : public virtual GrandParent;
class Parent2 : public virtual GrandParent;
class Child : public Parent1, public Parent2;

始终检查您的设计,确保您没有使用继承来节省数据重用.如果你可以用组合表示相同的东西(通常你可以),这是一个更好的方法.


如果你禁止继承而只使用组合,你总是在'Child`中有两个`GrandParent`.人们担心MI,因为人们只是认为他们可能不理解语言规则.但任何无法获得这些简单规则的人也无法编写一个非平凡的程序.

4> Eugene Yokot..:

请参阅w:多重继承.

多重继承受到了批评,因此没有用多种语言实现.批评包括:

复杂性增加

语义歧义通常归结为钻石问题.

无法从单个类中显式继承多次

继承顺序改变类语义.

使用C++/Java样式构造函数的语言中的多重继承会加剧构造函数和构造函数链接的继承问题,从而在这些语言中创建维护和可扩展性问题.在构造函数链接范例下,很难实现具有极大变化的构造方法的继承关系中的对象.

现代的解决方法是使用COM和Java接口等接口(纯抽象类).

我可以用其他东西代替这个吗?

是的你可以.我要从GoF偷走.

编程到接口,而不是实现

更喜欢继承的组合



5> David Thornl..:

公共继承是一种IS-A关系,有时一个类将是几个不同类的类型,有时反映这一点很重要.

"Mixins"有时也很有用.它们通常是小类,通常不从任何东西继承,提供有用的功能.

只要继承层次结构相当浅(因为它应该几乎总是如此),并且管理得当,您就不可能获得可怕的钻石继承.钻石对于使用多重继承的所有语言来说都不是问题,但是C++对它的处理经常是尴尬的,有时令人费解.

虽然我遇到了多重继承非常方便的情况,但它们实际上相当罕见.这可能是因为当我不需要多重继承时,我更喜欢使用其他设计方法.我更愿意避免混淆语言结构,并且很容易构建继承案例,你必须非常好地阅读手册以弄清楚发生了什么.



6> jwpfox..:

你不应该"避免"多重继承,但你应该意识到可能出现的问题,如"钻石问题"(http://en.wikipedia.org/wiki/Diamond_problem),并小心对待给你的力量,你应该拥有所有权力.

推荐阅读
手机用户2402852387
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有