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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/406081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:10:50  来源:igfitidea点击:

Why should I avoid multiple inheritance in C++?

c++oopmultiple-inheritance

提问by Hai

Is it a good concept to use multiple inheritance or can I do other things instead?

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

回答by paercebal

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. 考虑特征的组合,而不是继承
  2. 小心恐惧之钻
  3. 考虑继承多个接口而不是对象
  4. 有时,多重继承是正确的。如果是,那么使用它。
  5. 准备好在代码中捍卫您的多继承架构

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 Cardoes not need to inherit from an Engineto work, nor from a Wheel. A Carhas an Engineand four Wheel.

你的对象真的需要从另一个继承吗?ACar不需要从 an 继承Engine来工作,也不需要从 a继承Wheel。ACar有一个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 Band Cboth inherit from A. And (don't ask me why) someone then decides that Dmust inherit both from Band C.

通常情况下,你有一个类A,然后BC来自继承A。并且(不要问我为什么)然后有人决定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, Dshould not have inherited from both Band C), because this was bad architecture (in fact, Cshould not have existed at all...)
  2. How much maintainers were paying for that, because in C++, the parent class Awas present twice in its grandchild class D, and thus, updating one parent field A::fieldmeant either updating it twice (through B::fieldand C::field), or having something go silently wrong and crash, later (new a pointer in B::field, and delete C::field...)
  1. 从一开始就犯了多大的错误(在这两种情况下,D都不应该继承自BC),因为这是糟糕的架构(事实上,C根本不应该存在......)
  2. 维护人员为此付出了多少代价,因为在 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 Ato exist twice in your layout, and what does it mean? If yes, then by all means inherit from it twice.
  • if it should exist only once, then inherit from it virtually.
  • A在您的布局中存在两次是否可取,这意味着什么?如果是,那么一定要从它继承两次。
  • 如果它应该只存在一次,那么虚拟地继承它。

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 Aand Bis that users can use Cas if it was a A, and/or as if it was a B.

通常,当 C 继承自Aand时,您的意思B是用户可以将其C用作 aA和/或好像它是 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)
  2. no member variables
  1. 它的所有方法都声明为纯虚拟(后缀 = 0)(删除了 2017-05-03)
  2. 没有成员变量

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(i.e. no member variables, except 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.

首先,NVI 模式可用于生成接口,因为真正的标准是没有状态(即没有成员变量,除了this)。你的抽象接口的重点是发布一个合同(“你可以这样称呼我,这样称呼我”),仅此而已。只有抽象虚拟方法的限制应该是一种设计选择,而不是一种义务。

Second, in C++, it makes sense to inherit virtually from abstract interfaces, (even with the additional cost/indirection). If you don't, and the interface inheritance appears multiple time in your hierarchy, then you'll have ambiguities.

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

Third, object orientation is great, but it is not The Only Truth Out ThereTMin C++. Use the right tools, and always remember you have other paradigms in C++ offering different kind of solutions.

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

4. Do you really need Multiple Inheritance?

4. 你真的需要多重继承吗?

Sometimes, yes.

有时是的。

Usually, your Cclass is inheriting from Aand B, and Aand Bare two unrelated objects (i.e. not in the same hierarchy, nothing in common, different concepts, etc.).

通常,您的C类继承自Aand B,并且AandB是两个不相关的对象(即不在同一层次结构中,没有共同点,不同的概念等)。

For example, you could have a system of Nodeswith 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.

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

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 geoand the other being ai

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

So you have your own own::Nodederive both from ai::Agentand 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::Nodeis really really both a ai::Agentand a geo::Point, then composition will not do.

这是您应该问自己是否不应该使用组合的时刻。如果own::Node真的真的既是 aai::Agent又是 a geo::Point,那么组合就行不通了。

Then you'll need multiple inheritance, having your own::Nodecommunicate with other agents according to their position in a 3D space.

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

(You'll note that ai::Agentand geo::Pointare completely, totally, fully UNRELATED... This drastically reduces the danger of multiple inheritance)

(您会注意到,ai::Agent并且geo::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)
  • the virtual inheritance from std::exception (Is Virtual Inheritance necessary for Exceptions?)
  • etc.
  • 使用(希望是私有的)继承作为实现细节
  • 一些 C++ 惯用语如策略可以使用多重继承(当每个部分需要通过 与其他部分进行通信时this
  • 来自 std::exception 的虚拟继承(异常是否需要虚拟继承?
  • 等等。

Sometimes you can use composition, and sometimes MI is better. The point is: You have a choice. Do it responsibly (and have your code reviewed).

有时你可以使用组合,有时 MI 更好。重点是:你有选择。负责任地做(并您的代码)。

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 Carboth an Engineand a Wheel).

大多数时候,根据我的经验,没有。MI 不是正确的工具,即使它看起来有效,因为它可以被懒惰的人用来将特征堆在一起而没有意识到后果(例如同时制作CaranEngine和 a 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 很臭,所以准备在代码中捍卫你的架构(捍卫它是一件好事,因为如果你无法捍卫它,那么你就不应该这样做)。

回答by Nemanja Trifunovic

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

人们非常正确地说你不需要多重继承,因为你可以用多重继承做任何事情,你也可以用单继承做。你只需使用我提到的委托技巧。此外,您根本不需要任何继承,因为您使用单继承所做的任何事情也可以通过类转发而无需继承。实际上,您也不需要任何类,因为您可以使用指针和数据结构来完成这一切。但是你为什么要这样做呢?什么时候方便使用语言设施?你什么时候更喜欢一种解决方法?我见过多重继承很有用的情况,我什至见过相当复杂的多重继承很有用的情况。一般来说,我更喜欢使用语言提供的工具来做变通方法

回答by billybob

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.

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

回答by Eugene Yokota

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. Objects in inheritance relationships with greatly varying construction methods are hard to implement under the constructor chaining paradigm.

多重继承受到了批评,因此在许多语言中都没有实现。批评包括:

  • 增加的复杂性
  • 语义歧义常被概括为菱形问题
  • 无法从单个类显式继承多次
  • 继承顺序改变类语义。

具有 C++/Java 风格构造函数的语言中的多重继承加剧了构造函数和构造函数链的继承问题,从而在这些语言中产生了维护和可扩展性问题。构造方法差异很大的继承关系中的对象在构造函数链范式下很难实现。

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
  • 编程到接口,而不是实现
  • 优先组合而不是继承

回答by David Thornley

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.

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

回答by jwpfox

You shouldn't "avoid" multiple inheritance but you should be aware of problems that can arise such as the 'diamond problem' ( http://en.wikipedia.org/wiki/Diamond_problem) and treat the power given to you with care, as you should with all powers.

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

回答by Larry

We use Eiffel. We have excellent MI. No worries. No issues. Easily managed. There are times to NOT use 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).

我们使用埃菲尔。我们有出色的 MI。不用担心。没有问题。易于管理。有时不使用 MI。然而,它比人们意识到的更有用,因为他们:A)使用一种不能很好地管理它的危险语言 - 或 - B)对他们多年来围绕 MI 的工作方式感到满意 - 或 - C)其他原因(太多了,我很确定——请参阅上面的答案)。

For us, using Eiffel, MI is as natural as anything else and another fine tool in the toolbox. Frankly, we're quite unconcerned that no one else is using Eiffel. No worries. We are happy with what we have and invite you to have a look.

对我们来说,使用 Eiffel,MI 和其他任何东西一样自然,也是工具箱中的另一个好工具。坦率地说,我们并不担心没有其他人在使用 Eiffel。不用担心。我们对我们所拥有的感到满意,并邀请您来看看。

While you're looking: Take special note of Void-safety and the eradication of Null pointer dereferencing. While we're all dancing around MI, your pointers are getting lost! :-)

在您查看时:特别注意 Void 安全性和消除空指针取消引用。当我们都在 MI 周围跳舞时,您的指针却丢失了!:-)

回答by Ncat

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 Bderives from class A. Note that, given

意味着class B源自class A。请注意,给定

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 Athat trivially Aderives from A, thus our inheritance model fulfills the definition of a category. In more traditional language, we have a category Classwith objects all classes and morphisms the inheritance relations.

此外,我们说对于每个从A平凡A派生的类A,因此我们的继承模型满足类别的定义。在更传统的语言中,我们有一个类别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 Dinherits from all of A, B, and C. Furthermore, and getting closer to addressing OP's question, Dalso inherits from any superclass of A. We can draw a diagram

这是一个阴暗的图表,但它会做。所以D继承自所有A, B, 和C。此外,为了更接近解决 OP 的问题,D也继承自A. 我们可以画个图

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

Now, problems associated with the Diamond of Death here are when Cand Bshare some property/method names and things get ambiguous; however, if we move any shared behavior into Athen the ambiguity disappears.

现在,死亡的钻石相关的问题在这里是当CB大家分享一些属性/方法的名字和事情变得不明确; 然而,如果我们将任何共享行为移入,A那么歧义就会消失。

Put in categorical terms, we want A, Band Cto be such that if Band Cinherit from Qthen Acan be rewritten as as subclass of Q. This makes Asomething called a pushout.

放入分类方面,我们希望AB并且C是这样,如果BC继承Q,然后A可以改写为作为子类Q。这使得A称为pushout 的东西。

There is also a symmetric construction on Dcalled a pullback. This is essentially the most general useful class you can construct which inherits from both Band C. That is, if you have any other class Rmultiply inheriting from Band C, then Dis a class where Rcan be rewritten as as subclass of D.

还有一种对称结构D称为回撤。这本质上是您可以构造的最通用的有用类,它继承自BC。也就是说,如果您有任何其他类RBand乘以继承C,则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.

确保您的钻石提示是回调和推出,这为我们提供了一种很好的方式来一般处理可能会出现的名称冲突或维护问题。

NotePaercebal's answerinspired 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;DRThink of the inheritance relationships in your program as forming a category. 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.

TL;DR将程序中的继承关系视为形成一个类别。然后,您可以通过将多重继承的类推出并对称地制作一个共同的父类来避免厄运钻石问题。

回答by sfkleach

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. As a consequence there is a tendency to steer programmers away from this feature.

每种编程语言对面向对象编程的处理都略有不同,各有利弊。C++ 的版本将重点直接放在性能上,但伴随而来的缺点是编写无效代码非常容易,这对于多重继承也是如此。因此,有一种趋势是让程序员远离此功能。

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. 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). 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". e.g.

正如在 C++ 中经常发生的那样,如果您遵循基本准则,则可以安全地使用它,而不必经常“回头看”。关键思想是区分一种特殊的类定义,称为“混入”;如果 class 的所有成员函数都是虚拟的(或纯虚拟的),则它是一个混合。然后,您可以从单个主类和任意数量的“混入”继承,但您应该使用关键字“虚拟”继承混入。例如

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. And remember to make all the destructors virtual too.

我的建议是,如果您打算将一个类用作混合类,您还应采用命名约定,以便任何阅读代码的人都能轻松查看发生的情况并验证您是否遵守基本准则的规则. 如果你的 mix-ins 也有默认构造函数,你会发现它工作得更好,这仅仅是因为虚拟基类的工作方式。并记住将所有析构函数也设为虚拟。

Note that my use of the word "mix-in" here isn't the same as the parameterised template class (see this linkfor 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.

现在我不想给人这样的印象,即这是安全使用多重继承的唯一方法。这只是一种很容易检查的方法。

回答by CMS

You should use it carefully, there are some cases, like the Diamond Problem, when things can go complicated.

您应该谨慎使用它,在某些情况下,例如钻石问题,事情会变得复杂。

alt text
(source: learncpp.com)

替代文字
(来源:learncpp.com