简体   繁体   English

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

[英]Why should I avoid multiple inheritance in C++?

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

Multiple inheritance (abbreviated as MI) smells , which means that usually , it was done for bad reasons, and it will blow back in the face of the maintainer.多重继承(简称MI)的味道,这意味着通常,它是出于不好的原因而做的,它会在维护者面前反击。

Summary概括

  1. Consider composition of features, instead of inheritance考虑特征的组合,而不是继承
  2. Be wary of the Diamond of Dread小心恐惧之钻
  3. Consider inheritance of multiple interfaces instead of objects考虑继承多个接口而不是对象
  4. Sometimes, Multiple Inheritance is the right thing.有时,多重继承是正确的。 If it is, then use it.如果是,那么使用它。
  5. Be prepared to defend your multiple-inherited architecture in code reviews准备好在代码审查中捍卫您的多继承架构

1. Perhaps composition? 1. 也许是组合?

This is true for inheritance, and so, it's even more true for multiple inheritance.这对于继承来说是正确的,因此对于多重继承更是如此。

Does your object really needs to inherit from another?你的对象真的需要从另一个继承吗? A Car does not need to inherit from an Engine to work, nor from a Wheel . Car不需要从Engine继承来工作,也不需要从Wheel继承。 A Car has an Engine and four Wheel . Car有一个Engine和四个Wheel

If you use multiple inheritance to resolve these problems instead of composition, then you've done something wrong.如果您使用多重继承而不是组合来解决这些问题,那么您就做错了。

2. The Diamond of Dread 2. 恐惧钻石

Usually, you have a class A , then B and C both inherit from A .通常,您有一个类A ,然后BC都继承自A And (don't ask me why) someone then decides that D must inherit both from B and C .并且(不要问我为什么)然后有人决定D必须同时继承BC

I've encountered this kind of problem twice in 8 eights years, and it is amusing to see because of:我在八八年里遇到过两次这样的问题,很有趣,因为:

  1. How much of a mistake it was from the beginning (In both cases, D should not have inherited from both B and C ), because this was bad architecture (in fact, C should not have existed at all...)从一开始就犯了多大的错误(在这两种情况下, D都不应该从BC继承),因为这是糟糕的架构(实际上, C根本不应该存在......)
  2. How much maintainers were paying for that, because in C++, the parent class A was present twice in its grandchild class D , and thus, updating one parent field A::field meant either updating it twice (through B::field and C::field ), or having something go silently wrong and crash, later (new a pointer in B::field , and delete C::field ...)维护人员为此付出了多少,因为在 C++ 中,父类A在其孙类D出现两次,因此,更新一个父字段A::field意味着要么更新它两次(通过B::fieldC::field ),或者有一些事情默默地出错并崩溃,稍后(在B::field新建一个指针,并删除C::field ...)

Using the keyword virtual in C++ to qualify the inheritance avoids the double layout described above if this is not what you want, but anyway, in my experience, you're probably doing something wrong...如果这不是您想要的,则在 C++ 中使用关键字 virtual 来限定继承可避免上述双重布局,但无论如何,根据我的经验,您可能做错了什么......

In Object hierarchy, you should try to keep the hierarchy as a Tree (a node has ONE parent), not as a graph.在对象层次结构中,您应该尝试将层次结构保持为树(一个节点有一个父节点),而不是图形。

More about the Diamond (edit 2017-05-03)关于钻石的更多信息(编辑 2017-05-03)

The real problem with the Diamond of Dread in C++ ( assuming the design is sound - have your code reviewed! ), is that you need to make a choice : C++ 中 Dread of Dread 的真正问题(假设设计是合理的 - 检查您的代码! ),是您需要做出选择

  • Is it desirable for the class A to exist twice in your layout, and what does it mean? A类是否需要在您的布局中出现两次,这意味着什么? If yes, then by all means inherit from it twice.如果是,那么一定要从它继承两次。
  • if it should exist only once, then inherit from it virtually.如果它应该只存在一次,那么虚拟地继承它。

This choice is inherent to the problem, and in C++, unlike other languages, you can actually do it without dogma forcing your design at language level.这种选择是问题所固有的,在 C++ 中,与其他语言不同,您实际上可以做到这一点,而无需教条在语言级别强制您的设计。

But like all powers, with that power comes responsibility: Have your design reviewed.但与所有权力一样,这种权力也伴随着责任:审查您的设计。

3. Interfaces 3. 接口

Multiple inheritance of zero or one concrete classes, and zero or more interfaces is usually Okay, because you won't encounter the Diamond of Dread described above.零个或一个具体类的多重继承,以及零个或多个接口通常是可以的,因为您不会遇到上述的恐惧钻石。 In fact, this is how things are done in Java.事实上,这就是 Java 中的工作方式。

Usually, what you mean when C inherits from A and B is that users can use C as if it was a A , and/or as if it was a B .通常情况下,你是什么意思时由C继承AB是,用户可以使用C ,就好像它是A ,和/或,如果它是一个B

In C++, an interface is an abstract class which has:在 C++ 中,接口是一个抽象类,它具有:

  1. all its method declared pure virtual (suffixed by = 0) (removed the 2017-05-03)它的所有方法都声明为纯虚拟(后缀 = 0) (删除了 2017-05-03)
  2. no member variables没有成员变量

The Multiple inheritance of zero to one real object, and zero or more interfaces is not considered "smelly" (at least, not as much).零到一个真实对象和零个或多个接口的多重继承不被认为是“臭的”(至少,没有那么多)。

More about the C++ Abstract Interface (edit 2017-05-03)有关 C++ 抽象接口的更多信息(编辑 2017-05-03)

First, the NVI pattern can be used to produce an interface, because the real criteria is to have no state (ie no member variables, except this ).首先,NVI 模式可用于生成接口,因为真正的标准是没有状态(即没有成员变量,除了this )。 Your abstract interface's point is to publish a contract ("you can call me this way, and this way"), nothing more, nothing less.你的抽象接口的重点是发布一个合同(“你可以这样称呼我,这样称呼我”),仅此而已。 The limitation of having only abstract virtual method should be a design choice, not an obligation.只有抽象虚拟方法的限制应该是一种设计选择,而不是一种义务。

Second, in C++, it makes sense to inherit virtually from abstract interfaces, (even with the additional cost/indirection).其次,在 C++ 中,从抽象接口虚拟继承是有意义的(即使有额外的成本/间接)。 If you don't, and the interface inheritance appears multiple time in your hierarchy, then you'll have ambiguities.如果您不这样做,并且接口继承在您的层次结构中多次出现,那么您就会有歧义。

Third, object orientation is great, but it is not The Only Truth Out There TM in C++.三,面向对象是伟大的,但它不是唯一的真理在那里TM在C ++中。 Use the right tools, and always remember you have other paradigms in C++ offering different kind of solutions.使用正确的工具,并始终记住您在 C++ 中有其他范式提供不同类型的解决方案。

4. Do you really need Multiple Inheritance? 4. 你真的需要多重继承吗?

Sometimes, yes.有时是的。

Usually, your C class is inheriting from A and B , and A and B are two unrelated objects (ie not in the same hierarchy, nothing in common, different concepts, etc.).通常,您的C类是从AB继承的,而AB是两个不相关的对象(即不在同一层次结构中,没有共同点,不同的概念等)。

For example, you could have a system of Nodes with X,Y,Z coordinates, able to do a lot of geometric calculations (perhaps a point, part of geometric objects) and each Node is an Automated Agent, able to communicate with other agents.例如,您可以拥有一个具有 X、Y、Z 坐标的Nodes系统,能够进行大量几何计算(可能是一个点,几何对象的一部分),并且每个节点都是一个自动代理,能够与其他代理进行通信.

Perhaps you already have access to two libraries, each with its own namespace (another reason to use namespaces... But you use namespaces, don't you?), one being geo and the other being ai也许您已经可以访问两个库,每个库都有自己的命名空间(使用命名空间的另一个原因……但是您使用命名空间,不是吗?),一个是geo ,另一个是ai

So you have your own own::Node derive both from ai::Agent and geo::Point .所以你有你自己own::Node派生自ai::Agentgeo::Point

This is the moment when you should ask yourself if you should not use composition instead.这是您应该问自己是否不应该使用组合的时刻。 If own::Node is really really both a ai::Agent and a geo::Point , then composition will not do.如果own::Node真的既是ai::Agent又是geo::Point ,那么组合就行不通了。

Then you'll need multiple inheritance, having your own::Node communicate with other agents according to their position in a 3D space.然后你需要多重继承,让你own::Node根据他们在 3D 空间中的位置与其他代理进行通信。

(You'll note that ai::Agent and geo::Point are completely, totally, fully UNRELATED... This drastically reduces the danger of multiple inheritance) (你会注意到ai::Agentgeo::Point是完全、完全、完全无关的……这大大降低了多重继承的危险)

Other cases (edit 2017-05-03)其他案例(编辑 2017-05-03)

There are other cases:还有其他情况:

  • using (hopefully private) inheritance as implementation detail使用(希望是私有的)继承作为实现细节
  • some C++ idioms like policies could use multiple inheritance (when each part needs to communicate with the others through this )一些像策略这样的 C++ 习语可以使用多重继承(当每个部分都需要通过this与其他部分进行通信时)
  • the virtual inheritance from std::exception ( Is Virtual Inheritance necessary for Exceptions? )来自 std::exception 的虚拟继承(异常是否需要虚拟继承?
  • etc.等等。

Sometimes you can use composition, and sometimes MI is better.有时你可以使用组合,有时 MI 更好。 The point is: You have a choice.重点是:你有选择。 Do it responsibly (and have your code reviewed).负责任地做(并审查您的代码)。

5. So, should I do Multiple Inheritance? 5. 那么,我应该做多重继承吗?

Most of the time, in my experience, no.大多数时候,根据我的经验,没有。 MI is not the right tool, even if it seems to work, because it can be used by the lazy to pile features together without realizing the consequences (like making a Car both an Engine and a Wheel ). MI 不是正确的工具,即使它似乎有效,因为它可以被懒惰的人使用,而没有意识到后果(例如使Car既是Engine又是Wheel )。

But sometimes, yes.但有时,是的。 And at that time, nothing will work better than MI.那时,没有什么比 MI 更好用了。

But because MI is smelly, be prepared to defend your architecture in code reviews (and defending it is a good thing, because if you're not able to defend it, then you should not do it).但是因为 MI 很臭,所以准备在代码审查中捍卫你的架构(捍卫它是一件好事,因为如果你无法捍卫它,那么你就不应该这样做)。

From an interview with Bjarne Stroustrup :来自Bjarne Stroustrup采访

People quite correctly say that you don't need multiple inheritance, because anything you can do with multiple inheritance you can also do with single inheritance.人们非常正确地说你不需要多重继承,因为你可以用多重继承做任何事情,你也可以用单继承做。 You just use the delegation trick I mentioned.你只需使用我提到的委托技巧。 Furthermore, you don't need any inheritance at all, because anything you do with single inheritance you can also do without inheritance by forwarding through a class.此外,您根本不需要任何继承,因为您使用单继承所做的任何事情也可以通过类转发而无需继承。 Actually, you don't need any classes either, because you can do it all with pointers and data structures.实际上,您也不需要任何类,因为您可以使用指针和数据结构来完成这一切。 But why would you want to do that?但是你为什么要这样做呢? When is it convenient to use the language facilities?什么时候方便使用语言设施? When would you prefer a workaround?你什么时候更喜欢一种解决方法? I've seen cases where multiple inheritance is useful, and I've even seen cases where quite complicated multiple inheritance is useful.我见过多重继承很有用的情况,我什至见过相当复杂的多重继承很有用的情况。 Generally, I prefer to use the facilities offered by the language to doing workarounds一般来说,我更喜欢使用语言提供的工具来做变通方法

There's no reason to avoid it and it can be very useful in situations.没有理由避免它,它在某些情况下非常有用。 You need to be aware of the potential issues though.不过,您需要了解潜在的问题。

The biggest one being the diamond of death:最大的是死亡钻石:

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

You now have two "copies" of GrandParent within Child.您现在在 Child 中有两个 GrandParent 的“副本”。

C++ has thought of this though and lets you do virtual inheritence to get around the issues. C++ 已经想到了这一点,并允许您进行虚拟继承来解决这些问题。

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

Always review your design, ensure you are not using inheritance to save on data reuse.始终检查您的设计,确保您没有使用继承来节省数据重用。 If you can represent the same thing with composition (and typically you can) this is a far better approach.如果你可以用组合来表示同样的东西(通常你可以),这是一种更好的方法。

See w: Multiple Inheritance .请参阅 w:多重继承

Multiple inheritance has received criticism and as such, is not implemented in many languages.多重继承受到了批评,因此在许多语言中都没有实现。 Criticisms includes:批评包括:

  • Increased complexity增加的复杂性
  • Semantic ambiguity often summarized as the diamond problem .语义歧义常被概括为菱形问题
  • Not being able to explicitly inherit multiple times from a single class无法从单个类显式继承多次
  • Order of inheritance changing class semantics.继承顺序改变类语义。

Multiple inheritance in languages with C++/Java style constructors exacerbates the inheritance problem of constructors and constructor chaining, thereby creating maintenance and extensibility problems in these languages.具有 C++/Java 风格构造函数的语言中的多重继承加剧了构造函数和构造函数链的继承问题,从而在这些语言中产生了维护和可扩展性问题。 Objects in inheritance relationships with greatly varying construction methods are hard to implement under the constructor chaining paradigm.构造方法差异很大的继承关系中的对象在构造函数链范式下很难实现。

Modern way of resolving this to use interface (pure abstract class) like COM and Java interface.解决这个问题的现代方法是使用接口(纯抽象类),如 COM 和 Java 接口。

I can do other things in place of this?我可以做其他事情来代替这个吗?

Yes, you can.是的你可以。 I am going to steal from GoF .我要从GoF那里偷东西。

  • Program to an Interface, not an Implementation编程到接口,而不是实现
  • Prefer composition over inheritance优先组合而不是继承

Public inheritance is an IS-A relationship, and sometimes a class will be an type of several different classes, and sometimes it's important to reflect this.公共继承是一种 IS-A 关系,有时一个类会是几个不同类的类型,有时反映这一点很重要。

"Mixins" are also sometimes useful. “混合”有时也很有用。 They are generally small classes, usually not inheriting from anything, providing useful functionality.它们通常是小类,通常不继承任何东西,提供有用的功能。

As long as the inheritance hierarchy is fairly shallow (as it should almost always be), and well managed, you're unlikely to get the dreaded diamond inheritance.只要继承层次结构相当浅(它几乎总是如此)并且管理得当,您就不太可能获得可怕的菱形继承。 The diamond isn't a problem with all languages that use multiple inheritance, but C++'s treatment of it is frequently awkward and sometimes puzzling.菱形不是所有使用多重继承的语言的问题,但 C++ 对它的处理常常很尴尬,有时令人费解。

While I've run into cases where multiple inheritance is very handy, they're actually fairly rare.虽然我遇到过多重继承非常方便的情况,但它们实际上相当罕见。 This is likely because I prefer to use other design methods when I don't really need multiple inheritance.这可能是因为当我真的不需要多重继承时,我更喜欢使用其他设计方法。 I do prefer to avoid confusing language constructs, and it's easy to construct inheritance cases where you have to read the manual really well to figure out what's going on.我更喜欢避免混淆语言结构,并且很容易构建继承案例,您必须仔细阅读手册才能弄清楚发生了什么。

您不应该“避免”多重继承,但您应该意识到可能出现的问题,例如“钻石问题”( http://en.wikipedia.org/wiki/Diamond_problem )并谨慎对待赋予您的权力,就像你应该拥有所有权力一样。

At the risk of getting a bit abstract, I find it illuminating to think about inheritance within the frame of category theory.冒着变得有点抽象的风险,我发现在范畴论的框架内思考继承是很有启发性的。

If we think of all our classes and arrows between them denoting inheritance relations, then something like this如果我们考虑我们所有的类和它们之间的箭头表示继承关系,那么像这样

A --> B

means that class B derives from class A .意味着class B派生自class A Note that, given请注意,给定

A --> B, B --> C

we say C derives from B which derives from A, so C is also said to derive from A, thus我们说 C 派生自 B,而 B 派生自 A,所以 C 也被说成是从 A 派生的,因此

A --> C

Furthermore, we say that for every class A that trivially A derives from A , thus our inheritance model fulfills the definition of a category.此外,我们说,每类A是平凡的A派生自A ,因此,我们的遗传模型符合一类的定义。 In more traditional language, we have a category Class with objects all classes and morphisms the inheritance relations.在更传统的语言中,我们有一个类别Class其中包含所有类的对象和继承关系的态射。

That's a bit of setup, but with that let's take a look at our Diamond of Doom:这是一些设置,但让我们来看看我们的末日钻石:

C --> D
^     ^
|     |
A --> B

It's a shady looking diagram, but it'll do.这是一个阴暗的图表,但它会做。 So D inherits from all of A , B , and C .所以D继承自ABC Furthermore, and getting closer to addressing OP's question, D also inherits from any superclass of A .此外,为了更接近解决 OP 的问题, D还继承自A任何超类。 We can draw a diagram我们可以画个图

C --> D --> R
^     ^
|     |
A --> B
^ 
|
Q

Now, problems associated with the Diamond of Death here are when C and B share some property/method names and things get ambiguous;现在,与死亡之钻相关的问题是当CB共享一些属性/方法名称并且事情变得模棱两可时; however, if we move any shared behavior into A then the ambiguity disappears.然而,如果我们将任何共享行为移到A那么歧义就会消失。

Put in categorical terms, we want A , B and C to be such that if B and C inherit from Q then A can be rewritten as as subclass of Q .用分类术语来说,我们希望ABC是这样的,如果BC继承自Q那么A可以重写为Q子类。 This makes A something called a pushout .这使得A称为pushout

There is also a symmetric construction on D called a pullback . D上还有一个对称构造,称为回调 This is essentially the most general useful class you can construct which inherits from both B and C .这本质上是您可以构造的最通用的有用类,它继承自BC That is, if you have any other class R multiply inheriting from B and C , then D is a class where R can be rewritten as as subclass of D .也就是说,如果您有任何其他类RBC乘法继承,那么D是一个类,其中R可以重写为D子类。

Making sure your tips of the diamond are pullbacks and pushouts gives us a nice way to generically handle name-clashing or maintenance issues which might arise otherwise.确保您的钻石提示是回撤和推出,这为我们提供了一种很好的方式来一般性地处理可能出现的名称冲突或维护问题。

Note Paercebal 's answer inspired this as his admonitions are implied by the above model given that we work in the full category Class of all possible classes.注意Paercebal回答激发了这一点,因为鉴于我们在所有可能类别的完整类别 Class 中工作,上述模型暗示了他的警告。

I wanted to generalize his argument to something which shows how complicated multiple inheritance relationships can be both powerful and non-problematic.我想将他的论点概括为一些说明多重继承关系既强大又没有问题的复杂事物。

TL;DR Think of the inheritance relationships in your program as forming a category. TL;DR将程序中的继承关系视为形成一个类别。 Then you can avoid Diamond of Doom problems by making multiply-inherited classes pushouts and symmetrically, making a common parent class which is a pullback.然后,您可以通过将多重继承的类推出并对称地制作一个共同的父类来避免厄运钻石问题。

We use Eiffel.我们使用埃菲尔。 We have excellent MI.我们有出色的 MI。 No worries.不用担心。 No issues.没有问题。 Easily managed.易于管理。 There are times to NOT use MI.有时不使用 MI。 However, it useful more than people realize because they are: A) in a dangerous language that does not manage it well -OR- B) satisfied with how they've worked around MI for years and years -OR- C) other reasons (too numerous to list I am quite sure--see answers above).然而,它比人们意识到的更有用,因为他们:A)使用一种不能很好地管理它的危险语言 - 或 - B)对他们多年来围绕 MI 的工作方式感到满意 - 或 - C)其他原因(太多了,我很确定——请参阅上面的答案)。

For us, using Eiffel, MI is as natural as anything else and another fine tool in the toolbox.对我们来说,使用 Eiffel,MI 和其他任何东西一样自然,也是工具箱中的另一个好工具。 Frankly, we're quite unconcerned that no one else is using Eiffel.坦率地说,我们并不担心没有其他人在使用 Eiffel。 No worries.不用担心。 We are happy with what we have and invite you to have a look.我们对我们所拥有的感到满意,并邀请您来看看。

While you're looking: Take special note of Void-safety and the eradication of Null pointer dereferencing.在您查看时:特别注意 Void 安全性和消除空指针取消引用。 While we're all dancing around MI, your pointers are getting lost!当我们都在 MI 周围跳舞时,您的指针却丢失了! :-) :-)

Every programming language has a slightly different treatment of object-oriented programming with pros and cons.每种编程语言对面向对象编程的处理都略有不同,各有利弊。 C++'s version places the emphasis squarely on performance and has the accompanying downside that it is disturbingly easy to write invalid code - and this is true of multiple inheritance. C++ 的版本将重点直接放在性能上,但伴随而来的缺点是编写无效代码非常容易,这对于多重继承也是如此。 As a consequence there is a tendency to steer programmers away from this feature.因此,有一种趋势是让程序员远离此功能。

Other people have addressed the question of what multiple inheritance isn't good for.其他人已经解决了多重继承不适合的问题。 But we have seen quite a few comments that more-or-less imply that the reason to avoid it is because it's not safe.但是我们已经看到不少评论或多或少暗示避免它的原因是因为它不安全。 Well, yes and no.嗯,是和不是。

As is often true in C++, if you follow a basic guideline you can use it safely without having to "look over your shoulder" constantly.正如在 C++ 中经常发生的那样,如果您遵循基本准则,则可以安全地使用它,而不必经常“回头看”。 The key idea is that you distinguish a special kind of class definition called a "mix-in";关键思想是区分一种特殊的类定义,称为“混入”; class is a mix-in if all its member functions are virtual (or pure virtual).如果 class 的所有成员函数都是虚拟的(或纯虚拟的),则它是一个混合。 Then you are allowed to inherit from a single main class and as many "mix-ins" as you like - but you should inherit mixins with the keyword "virtual".然后,您可以从单个主类和任意数量的“混入”继承,但您应该使用关键字“虚拟”继承混入。 eg例如

class CounterMixin {
    int count;
public:
    CounterMixin() : count( 0 ) {}
    virtual ~CounterMixin() {}
    virtual void increment() { count += 1; }
    virtual int getCount() { return count; }
};

class Foo : public Bar, virtual public CounterMixin { ..... };

My suggestion is that if you intend to use a class as a mix-in class you also adopt a naming convention to make it easy for anyone reading the code to see what's happening & to verify you're playing by the rules of the basic guideline.我的建议是,如果您打算将一个类用作混合类,您还应采用命名约定,以便任何阅读代码的人都能轻松查看发生的情况并验证您是否遵守基本准则的规则. And you'll find it works much better if your mix-ins have default constructors too, just because of the way virtual base classes work.如果你的 mix-ins 也有默认构造函数,你会发现它工作得更好,这仅仅是因为虚拟基类的工作方式。 And remember to make all the destructors virtual too.并记住将所有析构函数也设为虚拟。

Note that my use of the word "mix-in" here isn't the same as the parameterised template class (see this link for a good explanation) but I think it is a fair use of the terminology.请注意,我在这里使用的“混入”一词与参数化模板类不同(请参阅此链接以获得很好的解释),但我认为这是对术语的合理使用。

Now I don't want to give the impression that this is the only way to use multiple inheritance safely.现在我不想给人这样的印象,即这是安全使用多重继承的唯一方法。 It's just one way that is fairly easy to check.这只是一种很容易检查的方法。

You should use it carefully, there are some cases, like the Diamond Problem , when things can go complicated.您应该谨慎使用它,在某些情况下,例如钻石问题,事情会变得复杂。

替代文字
(source: learncpp.com ) (来源: learncpp.com

The key issue with MI of concrete objects is that rarely do you have an object that legitimately should "Be an A AND be a B", so it is rarely the correct solution on logical grounds.具体对象的 MI 的关键问题是,您很少有一个合法地应该“成为 A 和成为 B”的对象,因此从逻辑上讲,它很少是正确的解决方案。 Far more often, you have an object C that obeys "C can act as an A or a B", which you can achieve via interface inheritance & composition.更常见的是,您有一个对象 C 遵守“C 可以充当 A 或 B”,您可以通过接口继承和组合来实现。 But make no mistake- inheritance of multiple interfaces is still MI, just a subset of it.但是不要搞错——多个接口的继承仍然是 MI,只是它的一个子集。

For C++ in particular, the key weakness of the feature isn't the actual EXISTENCE of Multiple Inheritance, but some constructs it allows that are almost always malformed.特别是对于 C++,该功能的主要弱点不是多重继承的实际存在,而是它允许的一些构造几乎总是格式错误的。 For example, inheriting multiple copies of the same object like:例如,继承同一个对象的多个副本,如:

class B : public A, public A {};

is malformed BY DEFINITION.根据定义是畸形的。 Translated into English this is "B is an A and an A".翻译成英文,这是“B is an A and an A”。 So, even in human language there's a severe ambiguity.因此,即使在人类语言中也存在严重的歧义。 Did you mean "B has 2 As" or just "B is an A"?.您的意思是“B 有 2 个 As”还是只是“B 是 A”? Allowing such pathological code, and worse making it a usage example, did C++ no favors when it came to making a case for keeping the feature in successor languages.允许这样的病态代码,更糟糕的是将其作为一个使用示例,当谈到在后继语言中保留该功能时,C++ 没有任何好处。

Uses and Abuses of Inheritance.继承的使用和滥用。

The article does a great job of explaining inheritance, and it's dangers.这篇文章很好地解释了继承,它是危险的。

Beyond the diamond pattern, multiple inheritance tends to make the object model harder to understand, which in turn increases maintenance costs.除了菱形模式之外,多重继承往往会使对象模型更难理解,从而增加维护成本。

Composition is intrinsically easy to understand, comprehend, and explain.构图本质上易于理解、理解和解释。 It can get tedious to write code for, but a good IDE (it's been a few years since I've worked with Visual Studio, but certainly the Java IDEs all have great composition shortcut automating tools) should get you over that hurdle.为其编写代码可能会很乏味,但是一个好的 IDE(我使用 Visual Studio 已经有几年了,但当然 Java IDE 都具有出色的组合快捷方式自动化工具)应该可以帮助您克服这个障碍。

Also, in terms of maintenance, the "diamond problem" comes up in non-literal inheritance instances as well.此外,在维护方面,“钻石问题”也出现在非文字继承实例中。 For instance, if you have A and B and your class C extends them both, and A has a 'makeJuice' method which makes orange juice and you extend that to make orange juice with a twist of lime: what happens when the designer for 'B' adds a 'makeJuice' method which generates and electrical current?例如,如果你有 A 和 B 并且你的类 C 扩展了它们,并且 A 有一个 'makeJuice' 方法来制作橙汁,然后你将它扩展为用酸橙制作橙汁:当设计者为 ' 时会发生什么B'添加了一个'makeJuice'方法来产生电流? 'A' and 'B' may be compatible "parents" right now , but that doesn't mean they will always be so! “A”和“B”可能是兼容的“父母”的权利,但并不意味着他们将永远是这样的!

Overall, the maxim of tending to avoid inheritance, and especially multiple inheritance, is sound.总的来说,避免继承,尤其是多重继承的格言是合理的。 As all maxims, there are exceptions, but you need to make sure that there is a flashing green neon sign pointing at any exceptions you code (and train your brain so that any time you see such inheritance trees you draw in your own flashing green neon sign), and that you check to make sure it all makes sense every once in a while.正如所有格言,都有例外,但您需要确保有一个闪烁的绿色霓虹灯指示您编写的任何异常(并训练您的大脑,以便每当您看到此类继承树时,您都会在自己闪烁的绿色霓虹灯中绘制签名),并且您每隔一段时间检查一下以确保这一切都有意义。

You can use composition in preference to inheritance.您可以优先使用组合而不是继承。

The general feeling is that composition is better, and it's very well discussed.总体感觉是构图更好,讨论得很好。

it takes 4/8 bytes per class involved.每个涉及的类需要 4/8 个字节。 (One this pointer per class). (每个类一个 this 指针)。

This might never be a concern, but if one day you have a micro data structure which is instanced billions of time it will be.这可能永远不会成为一个问题,但如果有一天你有一个被实例化数十亿次的微数据结构,它将会是。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM