views:

388

answers:

3
class A                     { public: void eat(){ cout<<"A";} }; 
class B: virtual public A   { public: void eat(){ cout<<"B";} }; 
class C: virtual public A   { public: void eat(){ cout<<"C";} }; 
class D: public         B,C { public: void eat(){ cout<<"D";} }; 

int main(){ 
    A *a = new D(); 
    a->eat(); 
} 

I understand the diamond problem, and above piece of code does not have that problem.

How exatly does virtual inheritance solve the problem?

What I understand: When I say A *a = new D();, the compiler wants to know if an object of type D can be assigned to a pointer of type A, but it has two paths that it can follow, but cannot decide by itself.

So, how does virtual inheritance resolve the issue (help compiler take the decision)?

+4  A: 

You want: (Achievable with virtual inheritance)

  D
  / \
B   C
  \ /
  A

And not: (What happens without virtual inheritance)

    D
   /   \
  B   C
  |     |
  A   A

Virtual inheritance means that there will be only 1 instance of the base A class not 2.

Your type D would have 2 vtable pointers (you can see them in the first diagram), one for B and one for C who virtually inherit A. D's object size is increased because it stores 2 pointers now; however there is only one A now.

So B::A and C::A are the same and so there can be no ambiguous calls from D. If you don't use virtual inheritance you have the second diagram above. And any call to a member of A then becomes ambiguous and you need to specify which path you want to take.

Wikipedia has another good rundown and example here

Brian R. Bondy
+2  A: 

Instances of derived classes "contain" instances of base classes, so they look in memory like that:

class A: [A fields]
class B: [A fields | B fields]
class C: [A fields | C fields]

Thus, without virtual inheritance, instance of class D would look like:

class D: [A fields | B fields | A fields | C fields | D fields]
          '- derived from B -' '- derived from C -'

So, note two "copies" of A data. Virtual inheritance means that inside derived class there is a vtable pointer set at runtime that points to data of base class, so that instances of B, C and D classes look like:

class B: [A fields | B fields]
          ^---------- pointer to A

class C: [A fields | C fields]
          ^---------- pointer to A

class D: [A fields | B fields | C fields | D fields]
          ^---------- pointer to B::A
          ^--------------------- pointer to C::A
el.pescado
+2  A: 

The problem is not the path the compiler must follow. The problem is the endpoint of that path: the result of the cast. When it comes to type conversions, the path does not matter, only the final result does.

If you use ordinary inheritance, each path has it's own distinctive endpoint, meaning that the result of the cast is ambiguous, which is the problem.

If you use virtual inheritance, you get a diamond-shaped hierarchy: both paths leads to the same endpoint. In this case the problem of choosing the path no longer exists (or, more precisely, no longer matters), because both paths lead to the same result. The result is no longer ambiguous - that is what matters. The exact path doesn't.

AndreyT
@Andrey: How does the compiler implement the inheritance...I mean I get your argument and I want to thank you for explaining it so lucidly..but it would really help if you can explain ( or point to a reference) as to how the compiler actually implements inheritance and what changes when I do virtual inheritance
Bruce