How can I avoid the Diamond of Death when using multiple inheritance? - c++
http://en.wikipedia.org/wiki/Diamond_problem
I know what it means, but what steps can I take to avoid it?
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 {};
virtual inheritance. That's what it's there for.
I'd stick to using multiple inheritance of interfaces only. While multiple inheritance of classes is attractive sometimes, it can also be confusing and painful if you rely on it regularly.
Inheritance is a strong, strong weapon. Use it only when you really need it. In the past, diamond inheritance was a sign that I was going to far with classification, saying that a user is an "employee" but they are also a "widget listener", but also a ...
In these cases, it's easy to hit multiple inheritance issues.
I solved them by using composition and pointers back to the owner:
Before:
class Employee : public WidgetListener, public LectureAttendee
{
public:
Employee(int x, int y)
WidgetListener(x), LectureAttendee(y)
{}
};
After:
class Employee
{
public:
Employee(int x, int y)
: listener(this, x), attendee(this, y)
{}
WidgetListener listener;
LectureAttendee attendee;
};
Yes, access rights are different, but if you can get away with such an approach, without duplicating code, it's better because it's less powerful. (You can save the power for when you have no alternative.)
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
In this the attributes of Class A repeated twice in Class D which makes more memory usage... So to save memory we make a virtual attribute for all inherited attributes of class A which are stored in a Vtable.
Well, the great thing about the Dreaded Diamond is that it's an error when it occurs. The best way to avoid is to figure out your inheritance structure beforehand. For instance, one project I work on has Viewers and Editors. Editors are logical subclasses of Viewers, but since all Viewers are subclasses - TextViewer, ImageViewer, etc., Editor does not derive from Viewer, thus allowing the final TextEditor, ImageEditor classes to avoid the diamond.
In cases where the diamond is not avoidable, using virtual inheritance. The biggest caveat, however, with virtual bases, is that the constructor for the virtual base must be called by the most derived class, meaning that a class that derives virtually has no control over the constructor parameters. Also, the presence of a virtual base tends to incur a performance/space penalty on casting through the chain, though I don't believe there is much of a penalty for more beyond the first.
Plus, you can always use the diamond if you are explicit about which base you want to use. Sometimes it's the only way.
I would suggest a better class design. I'm sure there are some problems that are solved best through multiple inheritance, but check to see if there is another way first.
If not, use virtual functions/interfaces.
Use inheritance by delegation. Then both classes will point to a base A, but have to implement methods that redirect to A. It has the side effect of turning protected members of A into "private" members in B,C, and D, but now you don't need virtual, and you don't have a diamond.
This is all I have in my notes about this topic. I think this would help you.
The diamond problem is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a member in A that B and C, and D does not override it, then which member does D inherit: that of B, or that of C?
struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
struct D : B, C {};
D d;
d.a = 10; //error: ambiguous request for 'a'
In the above example, both B & C inherit A, and they both have a single copy of A. However D inherits both B & C, therefore D has two copies of A, one from B and another from C. If we need to access the data member an of A through the object of D, we must specify the path from which the member will be accessed: whether it is from B or C because most compilers can’t differentiate between two copies of A in D.
There are 4 ways to avoid this ambiguity:
1- Using the scope resolution operator we can manually specify the path from which a data member will be accessed, but note that, still there are two copies (two separate subjects) of A in D, so there is still a problem.
d.B::a = 10; // OK
d.C::a = 100; // OK
d.A::a = 20; // ambiguous: which path the compiler has to take D::B::A or D::C::A to initialize A::a
2- Using static_cast we can specify which path the compiler can take to reach to data member, but note that, still there are two copies (two separate suobjects) of A in D, so there is still a problem.
static_cast<B&>(static_cast<D&>(d)).a = 10;
static_cast<C&>(static_cast<D&>(d)).a = 100;
d.A::a = 20; // ambiguous: which path the compiler has to take D::B::A or D::C::A to initialize A::a
3- Using overridden, the ambiguous class can overriden the member, but note that, still there are two copies (two separate suobjects) of A in D, so there is still a problem.
struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
struct D : B, C { int a; };
D d;
d.a = 10; // OK: D::a = 10
d.A::a = 20; // ambiguous: which path the compiler has to take D::B::A or D::C::A to initialize A::a
3- Using virtual inheritance, the problem is completely solved: If the inheritance from A to B and the inheritance from A to C are both marked "virtual", C++ takes special care to create only one A subobject,
struct A { int a; };
struct B : virtual A { int b; };
struct C : virtual A { int c; };
struct D : B, C {};
D d;
d.a = 10; // OK: D has only one copy of A - D::a = 10
d.A::a = 20; // OK: D::a = 20
Note that "both" B and C have to be virtual, otherwise if one of them is non-virtual, D would have a virtual A subobject and another non-virtual A subobject, and ambiguity will be still taken place even if class D itself is virtual. For example, class D is ambiguous in all of the following:
struct A { int a; };
struct B : A { int b; };
struct C : virtual A { int c; };
struct D : B, C {};
Or
struct A { int a; };
struct B : virtual A { int b; };
struct C : A { int c; };
struct D : B, C {};
Or
struct A { int a; };
struct B : A { int b; };
struct C : virtual A { int c; };
struct D : virtual B, C {};
Or
struct A { int a; };
struct B : virtual A { int b; };
struct C : A { int c; };
struct D : virtual B, C {};
Related
C++ general question on OOP Design, how to access member of object in the top of hierarchy from bottom
I have a Class A, in the class there is some important member (lets call it someVeryImportantNumber) and also objects of class B. In class B there are objects of class C and so on..., similar to a tree structure (could be 4, 10, or 20 levels of such objects.) How could i get access to someVeryImportantNumber from the bottom of the hierarchy (to access someVeryImportantNumber from class D). I was thinking about passing the number down the hierarchy, but this seems not very effective approach when i have lets say 10 or more of levels hierarchy. Is there some smarter way to do it? I looked at dependency injection but it is not the way to go for me... Any suggestions? Thank you... class D { public: void foo() { // need to use someVeryImportantNumber here } } class C { public: D d1; D d2; D d3; } class B { public: C c1; C c2; } class A { public: int someVeryImportantNumber = 1234; B b; } int main() { A a; return 0; }
You can use a reference to avoid copying that someVeryImportantNumber, and pass it through the constructors: class D { const int& someVeryImportantNumber_; public: D(const int& someVeryImportantNumber) : someVeryImportantNumber_(someVeryImportantNumber) {} void foo() { // need to use someVeryImportantNumber here } } class C { public: C(const int& someVeryImportantNumber) : d1(someVeryImportantNumber), d2(someVeryImportantNumber), d3(someVeryImportantNumber) {} D d1; D d2; D d3; } class B { public: B(const int& someVeryImportantNumber) : c1(someVeryImportantNumber), c2(someVeryImportantNumber) {} C c1; C c2; } class A { public: A() : b(someVeryImportantNumber) {} int someVeryImportantNumber = 1234; B b; } int main() { A a; return 0; }
You cannot do what you're asking. The reason is simple: in your example, D is contained in C as 3 separate instances, and there is no way to tell, from an object of type D, which instance it belongs to (d1, d2, or d3). On the other hand, you could access the container instance through something like container_of macro, provided that you also know which instance D belongs to. See https://radek.io/2012/11/10/magical-container_of-macro/. Notice that container_of is used in C but it works in C++ as well. For instance, if you know that your D is accessed from C::d1, you could use container_of(this, C, d1) to access the instance of C and then reiterate the procedure to access B and then A. But remember, there is no way to know at run time which one of the 3 instances of D your this pointer refers to, unless you somehow encode the information in your classes.
Avoid calling base constructor 2 times
Say I have several classes that inherit like so. #include <iostream> struct A { A() {std::cout << "a";} }; struct B : A {}; struct C : A {}; struct D : B, C {}; int main { D d; } On executing the program, as expected, I see two A objects were constructed, one for the B and one for the C object that are constructed when creating a D object. Whoever, how can I not create two A objects? I want the same A object to be used to create B and C objects. Is this possible?
If B and C both use virtual inheritance for A, then there will only be a single base class object for each D object: struct B : virtual A {}; struct C : virtual A {}; //... D d; //prints "a" rather than "aa" Live demo
If you're looking for a non-polymorphic solution, you're out of luck. Otherwise, change to the following: struct B : virtual A {}; struct C : virtual A {};
As written, every D object has two sub-objects of type A, one inherited from class B and one inherited from class C. So the constructor of A must run twice, once for each sub-object. If the original design is wrong, and there should only be one A sub-object, despite the use of two bases B and C, the change is to make A a virtual base of B and a virtual base of C. That way there will only be one A sub-object in a D object, and the constructor of A will only run once when a D object is constructed.
C++ Diamond of Death
Ok so I understand how to solve the problem of the diamond of death inheritance when you have full control over all the classes but what if you only have control over the last class to inherit from both So I have this: class A {}; class B : public A {}; class C : public A {}; class D : public B, public C {}; and I have no way of editing B and C only D, is there an easy way I can do this?
The is a very good reason you can't force B and C to share A. Consider: struct A {int i;}; struct B : A { B(){i=3;} void foo() { //crash if i!=3 } }; struct C : A { C(){i=4;} void bar() { //crash if i!=4 } }; B and C are good classes. They can't handle a situation that they won't get in (invalid value of i). If there was a way to do what you ask for (struct D:B,C, where B and C share A), what will be the value of D::A::i? virtual inheritance means "I need this class, but I won't require some valid values for it, and I completely fine with somebody else mess with it".
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.
Where is the "virtual" keyword necessary in a complex multiple inheritance hierarchy?
I understand the basics of C++ virtual inheritance. However, I'm confused about where exactly I need to use the virtual keyword with a complex class hierarchy. For example, suppose I have the following classes: A / \ B C / \ / \ D E F \ / \ / G H \ / I If I want to ensure that none of the classes appear more than once in any of the subclasses, which base classes need to be marked virtual? All of them? Or is it sufficient to use it only on those classes that derive directly from a class that may otherwise have multiple instances (i.e. B, C, D, E and F; and G and H (but only with the base class E, not with the base classes D and F))?
I toyed a program together which could help you to study the intricacies of virtual bases. It prints the class hierarchy under I as a digraph suitable for graphiviz ( http://www.graphviz.org/ ). There's a counter for each instance which helps you to understand the construction order as well. Here's the programm: #include <stdio.h> int counter=0; #define CONN2(N,X,Y)\ int id; N() { id=counter++; }\ void conn() \ {\ printf("%s_%d->%s_%d\n",#N,this->id,#X,((X*)this)->id); \ printf("%s_%d->%s_%d\n",#N,this->id,#Y,((Y*)this)->id); \ X::conn(); \ Y::conn();\ } #define CONN1(N,X)\ int id; N() { id=counter++; }\ void conn() \ {\ printf("%s_%d->%s_%d\n",#N,this->id,#X,((X*)this)->id); \ X::conn(); \ } struct A { int id; A() { id=counter++; } void conn() {} }; struct B : A { CONN1(B,A) }; struct C : A { CONN1(C,A) }; struct D : B { CONN1(D,B) }; struct E : B,C { CONN2(E,B,C) }; struct F : C { CONN1(F,C) }; struct G : D,E { CONN2(G,D,E) }; struct H : E,F { CONN2(H,E,F) }; struct I : G,H { CONN2(I,G,H) }; int main() { printf("digraph inh {\n"); I i; i.conn(); printf("}\n"); } If I run this (g++ base.cc ; ./a.out >h.dot ; dot -Tpng -o o.png h.dot ; display o.png), I get the typical non-virtual base tree: Adding enough virtualness... struct B : virtual A { CONN1(B,A) }; struct C : virtual A { CONN1(C,A) }; struct D : virtual B { CONN1(D,B) }; struct E : virtual B, virtual C { CONN2(E,B,C) }; struct F : virtual C { CONN1(F,C) }; struct G : D, virtual E { CONN2(G,D,E) }; struct H : virtual E,F { CONN2(H,E,F) }; struct I : G,H { CONN2(I,G,H) }; ..results in the diamond shape (look at the numbers to learn the construction order!!) But if you make all bases virtual: struct A { int id; A() { id=counter++; } void conn() {} }; struct B : virtual A { CONN1(B,A) }; struct C : virtual A { CONN1(C,A) }; struct D : virtual B { CONN1(D,B) }; struct E : virtual B, virtual C { CONN2(E,B,C) }; struct F : virtual C { CONN1(F,C) }; struct G : virtual D, virtual E { CONN2(G,D,E) }; struct H : virtual E, virtual F { CONN2(H,E,F) }; struct I : virtual G,virtual H { CONN2(I,G,H) }; You get a diamond with a different initialization order: Have fun!
You have to specify virtual inheritance when inheriting from any of A, B, C, and E classes (that are at the top of a diamond). class A; class B: virtual A; class C: virtual A; class D: virtual B; class E: virtual B, virtual C; class F: virtual C; class G: D, virtual E; class H: virtual E, F; class I: G, H;
My personal suggestion would be to start at B and C : virtual A, and then keep adding until the compiler stops complaining. In reality, I'd say that B and C : virtual A, G and H : virtual E, and E : virtual B and C. All the other inheritance links can be normal inheritance. This monstrosity would take like six decades to make a virtual call, though.
If you want to make sure that an object of the top class in the hierarchy (I in your case) contains exactly one subobject of each parent class, you have to find all classes in your hierarchy that have more than one superclass and make these classes virtual bases of their superclasses. That's it. In your case classes A, B, C and E have to become virtual base classes every time you inherit from them in this hierarchy. Classes D, F, G and H don't have to become virtual base classes.
If you want to have only one "physical" instance of each type for each instance of each type (only one A, only one B etc.) You'll just have to use virtual inheritance each time you use inheritance. If you want separate instances of one of the types, use normal inheritance.
Edited: I thought A was the most derived class ;) #Luther's answer really cool, but back to the original question: You NEED to use virtual inheritance when inheriting from any class from which at least one other class inherits in the inheritance hierarchy (in Luther's diagrams it means at least two arrows point to the class). Here it's unnecessary before D, F, G and H because only one class derives from them (and none derives from I at the moment). However, if you don't know beforehand whether or not another class will inherit from your base class, you can add in virtual as a precaution. For example it's recommended for an Exception class to inherit virtually from std::exception by no other than Stroustrup himself. As Luther has noted, it modifies the instantiation order (and has a slight effect on performances) but I would consider any design relying on the construction order to be wrong to begin with. And just as a precision: you still have the guarantees that base classes are initialized before any attribute of the derived class, and therefore before the execution of the derived's constructor body.
On thing to keep in mind is C++ keeps a table of the inheritance. The more you add virtual classes, the longer will be compilation time (linkage) and the heavier would be the runtime. In general, if one can avoid virtual class, you can replace by some templates or try to decouple in some way.
C++ shared objects
I have got four classes A, B, C and D. Class A has a member b of class B. Class B has a member c of class C. A has a member D* dpointer; This hierarchy has to be preserved (in fact this is a GUI with app, window, panel as A, B and C). Now B and C must use a method from *dpointer. Is there something more elegant than giving dpointer as a member of B and C ? Is it bad ?
Not directly, but you could put D inside of a shared_ptr<D>, which would alleviate any memory management headaches you might possibly have.
In this situation you should probably pass a reference to B and C instead of a pointer. As #Billy ONeil says in his answer, you should use a shared_ptr or a scoped_ptr if possible and appropriate (cannot judge without knowing more about D and dpointer) in A. Passing a reference to B and C has the advantage of making clear that these two merely use the D-object, but do not control it's lifecycle, and that an instance of D is required to use these classes (with a pointer NULL would be an option). If B and C only call const methods on D, you can even pass a const reference.
struct D; struct CommonABC { CommonABC(D * & dpointer) : dpointer(dpointer) {} D * & dpointer; }; struct C : CommonABC { C (const CommonABC & common) : CommonABC(сommon) {} }; struct B: CommonABC { B (const CommonABC & common) : CommonABC(сommon), c(common) {} C c; }; struct A { A () : b(CommonABC(dpointer)) {} D * dpointer; B b; };
In practice, I would probably opt for the shared_ptr solution mentioned above. But here is another way that is not often covered in the C++ literature, of the sort you might find in an interview question or a BrainBench test: struct D{ D(int val); void foo(); }; struct C:private virtual D{ void bar(){ foo(); } }; struct B:private C,private virtual D{ void car(){ foo(); } }; struct A:private B,private virtual D{ A(int val):D(val){} void aar(){ car(); foo(); } }; private inheritance implements the has-a relationship, just like making it a member. The only difference is that you can only have one of each type. In this case the same D object is shared by all classes in the composition. But if you want others to be able to understand what you are doing, go with the shared_ptrs.