How can I avoid the Diamond of Death when using multiple inheritance?
A practical example:
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
Notice how class D inherits from both B & C. But both B & C inherit from A. That will result in 2 copies of the class A being included in the vtable.
To solve this, we need virtual inheritance. It's class A that needs to be virtually inherited. So, this will fix the issue:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
Avoiding a diamond inheritance
What is the reason you want/need to avoid multiple inheritance?
Either way, if you cannot (do not want to) use inheritance in some of these cases, use encapsulation:
class A..
class `B`: public virtual `A`..
class `C`: public virtual `A`..
class `D`: public B {
private:
C c_instance_;
};
class `E`: public B {
C c_instance_;
};
...
To avoid code duplication, you can create a C (or D) holder and inherit it through protected inheritance and CRTP. This way a separate base class holds the C base functionality but the classes that inherit from it do not have a common base class:
class A..
class B: public A..
class C: public A..
template<class Specialized, class DependedOn>
class DependsOn {
protected:
DependedOn& get() { return implementation_; }
private:
DependedOn implementation_;
};
class D: public B, protected DependsOn<D, C> .. // inheritance with CRTP
class E: public B, protected DependsOn<E, C> .. // inheritance with CRTP
class FDB: public D, protected DependsOn<FDB, B> ..
D
, E
, FDB
and so on, will use the get()
method internally (or more specifically, DependsOn<D, C>::get()
, DependsOn<E, C>::get()
and so on) to implement the public interface.
Since DependsOn<D, C>
is different from DependsOn<E, C>
, you only have a common interface implemented by the whole hierarchy.
Do interfaces solve the deadly diamond of death issue?
Java prevents multiple concrete/abstract class inheritance, but not multiple interface inheritance. With multiple interface inheritance you inherit abstract methods, not implementation. See this post with a good explanation and examples: https://web.archive.org/web/20120724032720/http://www.tech-knowledgy.com/interfaces-sovles-diamond-death/
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.
Summary
- Consider composition of features, instead of inheritance
- Be wary of the Diamond of Dread
- Consider inheritance of multiple interfaces instead of objects
- Sometimes, Multiple Inheritance is the right thing. If it is, then use it.
- Be prepared to defend your multiple-inherited architecture in code reviews
1. Perhaps composition?
This is true for inheritance, and so, it's even more true for multiple inheritance.
Does your object really need to inherit from another? A Car
does not need to inherit from an Engine
to work, nor from a Wheel
. A Car
has an Engine
and four Wheel
.
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
, then B
and C
both inherit from A
. And (don't ask me why) someone then decides that D
must inherit both from B
and C
.
I've encountered this kind of problem twice in eight years, and it is amusing to see because of:
- How much of a mistake it was from the beginning (In both cases,
D
should not have inherited from bothB
andC
), because this was bad architecture (in fact,C
should not have existed at all...) - How much maintainers were paying for that, because in C++, the parent class
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:
- Is it desirable for the class
A
to 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.
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
and B
is that users can use C
as if it was an A
, and/or as if it was a B
.
In C++, an interface is an abstract class which has:
all its method declared pure virtual (suffixed by = 0)(removed the 2017-05-03)- 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)
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 methods 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 times 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 kinds of solutions.
4. Do you really need Multiple Inheritance?
Sometimes, yes.
Usually, your C
class is inheriting from A
and B
, and A
and B
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 being ai
So you have your own own::Node
derive both from ai::Agent
and geo::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.
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
and geo::Point
are completely, totally, fully UNRELATED... This drastically reduces the danger of multiple inheritance)
Other cases (edit 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.
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 an Engine
and a Wheel
).
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).
C++ Diamond of Death
Fake it by containment. Have D contain B and C and give D the same public interface as the union of B and C's public interface.
Then call the appropriate methods of B and C from D's public interface.
Of course you will have a problem casting and polymorphism as it won't follow the laws of inheritance.
In short, there's no good way.
Confused on C++ multiple inheritance
"multiple inheritance is typically a sign of a bad code design" - parents that are pure interfaces are not counted in regards to this rule. Your I*
classes are pure interfaces (only contain pure virtual functions) so you Digital*Point
classes are OK in this respect
Deadly Diamond of Death in Coq
I believe there is no way of convincing Coq that this is OK. However, you achieve a similar effect by using other features, such as canonical structures or type classes. This is how one could translate your example with type classes, for instance:
Class R1 T :=
{
op1 : T -> T
}.
Class R2 T :=
{
op2 : T -> T
}.
Class R12 T `{R1 T} `{R2 T} :=
{}.
Section S.
Context T `{R12 T}.
Variable x : T.
Hypothesis id1 : op1 x = x.
Hypothesis id2 : op2 x = x.
End S.
Notice that class R12
doesn't have any methods of its own, but requires type T
to be an instance both of R1
and R2
, which results morally in the same thing. The Context
declaration forces Coq to assume instances for R1
and R2
automatically, in virtue of those requirements.
Edit:
If you try to add an R0
class to complete the diagram, you may get a weird error resulting from a failing type-class inference. One solution is to turn off the automatic generalization in R12
(i.e., remove the back quotes), and force R1
and R2
to be based on the same R0
instance:
Class R0 (T : Type) := {}.
Class R1 T `{R0 T} :=
{
op1 : T -> T
}.
Class R2 T `{R0 T} :=
{
op2 : T -> T
}.
Class R12 T `{R0 T} {r1:R1 T} {r2:R2 T} :=
{}.
Section S.
Context T `{R12 T}.
Variable x : T.
Hypothesis id1 : op1 x = x.
Hypothesis id2 : op2 x = x.
End S.
Unfortunately, I don't really have a good explanation to why an error could occur there, as type class inference is somewhat complex. However, I think it is not related to the problem you had had first, because there aren't actually ambiguous paths like before.
Multiple inheritance in Java or not?
However how is it possible to inherit from Object and from any other
class at the same time? Isn't that a multiple inheritance.
No this is not what happens. Not all classes directly extend from Object
class. But only the class at the top level of inheritance hierarchy extends from Object
class(implicitly). Rest of the classes lower in the hierarchy, extends from the Object
class through the super classes. And, this is what we call multi-level inheritance.
So, consider the below hierarchy: -
class A { }
class B extends A { }
In the above case, class A
is equivalent to class A extends Object
.
Secondly, what for do we need to inherit all 11 Object methods? I
could hardly imagine why do I need it them in I/O
I suspect you meant override when you say inherit. You don't need to override any method of Object
class. It's just on your requirement, whether to override any method or not. For e.g.: - You would often want to override equals()
method, so as to write custom equality test for your instances. And in that case, you should also override the hashCode()
method, to maintain the contract of equals()
and hashCode()
.
Finally JDK 8 is going to offer us default methods realization in
interfaces and if which would probably cause multiple inheritance in
Java.What if interface A provides method a() with default realization and
interface B provides also a() method with another default realization
and our custom class C implements both interfaces and rely on default
realization - wouldn't that be Diamond of Death ?
I can't comment on this concept, because I haven't read about this thing yet. Probably, I would update the answer sometime later.
C++ diamond problem - How to call base method only once
You are asking for something like inheritance on a function level that automatically calls the inherited function and just adds more code. Also you want it to be done in a virtual way just like class inheritance. Pseudo syntax:
class Swimmer : public virtual Creature
{
public:
// Virtually inherit from Creature::print and extend it by another line of code
void print() : virtual Creature::print()
{
std::cout << "I can swim" << std::endl;
}
};
class Flier : public virtual Creature
{
public:
// Virtually inherit from Creature::print and extend it by another line of code
void print() : virtual Creature::print()
{
std::cout << "I can fly" << std::endl;
}
};
class Duck : public Flier, public Swimmer
{
public:
// Inherit from both prints. As they were created using "virtual function inheritance",
// this will "mix" them just like in virtual class inheritance
void print() : Flier::print(), Swimmer::print()
{
std::cout << "I'm a duck" << std::endl;
}
};
So the answer to your question
Is there some built-in way to do this?
is no. Something like this does not exist in C++. Also, I'm not aware of any other language that has something like this. But it is an interesting idea...
Related Topics
Are Child Processes Created with Fork() Automatically Killed When the Parent Is Killed
Char!=(Signed Char), Char!=(Unsigned Char)
Find Out Whether a C++ Object Is Callable
Why Does Int*[] Decay into Int** But Not Int[][]
C++ Array Assignment of Multiple Values
Linking a Shared Library with Another Shared Lib in Linux
Seeking and Reading Large Files in a Linux C++ Application
Shared-Memory Ipc Synchronization (Lock-Free)
Are Inner Classes in C++ Automatically Friends
When How to Use Explicit Operator Bool Without a Cast
How to Sort an Stl Map by Value
C++ Function Pointer (Class Member) to Non-Static Member Function
C and C++ Programming on Ubuntu 11.10
Std::String::C_Str() and Temporaries