Is it a good concept to use multiple inheritance or can I do other things instead?
相关问题
- how to define constructor for Python's new Nam
- Sorting 3 numbers without branching [closed]
- How to compile C++ code in GDB?
- Keeping track of variable instances
- Why does const allow implicit conversion of refere
相关文章
- 接口B继承接口A,但是又不添加新的方法。这样有什么意义吗?
- Class layout in C++: Why are members sometimes ord
- How to mock methods return object with deleted cop
- Which is the best way to multiply a large and spar
- C++ default constructor does not initialize pointe
- Selecting only the first few characters in a strin
- What exactly do pointers store? (C++)
- Converting glm::lookat matrix to quaternion and ba
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.
Summary
1. Perhaps composition?
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 anEngine
to work, nor from aWheel
. ACar
has anEngine
and fourWheel
.If you use multiple inheritance to resolve these problems instead of composition, then you've done something wrong.
2. The Diamond of Dread
Usually, you have a class
A
, thenB
andC
both inherit fromA
. And (don't ask me why) someone then decides thatD
must inherit both fromB
andC
.I've encountered this kind of problem twice in 8 eights years, and it is amusing to see because of:
D
should not have inherited from bothB
andC
), because this was bad architecture (in fact,C
should not have existed at all...)A
was present twice in its grandchild classD
, and thus, updating one parent fieldA::field
meant either updating it twice (throughB::field
andC::field
), or having something go silently wrong and crash, later (new a pointer inB::field
, and deleteC::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...
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)
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:
A
to exist twice in your layout, and what does it mean? If yes, then by all means inherit from it twice.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.
But like all powers, with that power comes responsibility: Have your design reviewed.
3. Interfaces
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.
Usually, what you mean when C inherits from
A
andB
is that users can useC
as if it was aA
, and/or as if it was aB
.In C++, an interface is an abstract class which has:
all its method declared pure virtual (suffixed by = 0)(removed the 2017-05-03)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)
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.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.
Third, object orientation is great, but it is not The Only Truth Out ThereTM in C++. Use the right tools, and always remember you have other paradigms in C++ offering different kind of solutions.
4. Do you really need Multiple Inheritance?
Sometimes, yes.
Usually, your
C
class is inheriting fromA
andB
, andA
andB
are two unrelated objects (i.e. not in the same hierarchy, nothing in common, different concepts, etc.).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.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 beingai
So you have your own
own::Node
derive both fromai::Agent
andgeo::Point
.This is the moment when you should ask yourself if you should not use composition instead. If
own::Node
is really really both aai::Agent
and ageo::Point
, then composition will not do.Then you'll need multiple inheritance, having your
own::Node
communicate with other agents according to their position in a 3D space.(You'll note that
ai::Agent
andgeo::Point
are completely, totally, fully UNRELATED... This drastically reduces the danger of multiple inheritance)Other cases (edit 2017-05-03)
There are other cases:
this
)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).
5. So, should I do Multiple Inheritance?
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 anEngine
and aWheel
).But sometimes, yes. And at that time, nothing will work better than 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).
From an interview with Bjarne Stroustrup:
You should use it carefully, there are some cases, like the Diamond Problem, when things can go complicated.
alt text http://www.learncpp.com/images/CppTutorial/Section11/PoweredDevice.gif
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).
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.
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! :-)
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:
You now have two "copies" of GrandParent within Child.
C++ has thought of this though and lets you do virtual inheritence to get around the issues.
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.
Modern way of resolving this to use interface (pure abstract class) like COM and Java interface.
Yes, you can. I am going to steal from GoF.