Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex diamond issue: C++ virtual inheritance

I have a diamond problem which look like this:

    __ A
  /    |\
 |  B  |  \
v|/v v\|v  \v
 B2   B3    C
  \v  /v   /
    B4    /
     \   /
       D

I tried many way to make the best virtual inheritance to get no duplicates but I couldn't find a solution. The class A contains a position. Here's a sample output:

Call: A() position pointer is: 0x2203be8
Call: B()
Call: B2() position pointer is: 0x2203be8
Call: B3() position pointer is: 0x2203be8
Call: C() position pointer is: 0x2203a28
Call: B4() position pointer is: 0x2203be8
Call: D() position pointer is: 0x2203a28

Why does D and C don't have the same pointer for position? Why there's no constructor for this A::position? What virtual inheritance should I make to solve this? Thanks.

EDIT:

Here's a code sample:

class A;
class B;
class B2 : public virtual B, public virtual A;
class B3 : public virtual B, public virtual A;
class C : public virtual A;
class B4 : public virtual B2, public virtual B3;
class D : public B4, public C;

EDIT 2: To make the output, I put this code inside each constructors:

A::A()
{
    std::cerr << "Call: A() position pointer is: " << &_position << std::endl;
}
like image 492
Guillaume Racicot Avatar asked Nov 16 '12 19:11

Guillaume Racicot


People also ask

How does virtual inheritance solve the diamond problem?

The Diamond Problem is fixed using virtual inheritance, in which the virtual keyword is used when parent classes inherit from a shared grandparent class. By doing so, only one copy of the grandparent class is made, and the object construction of the grandparent class is done by the child class.

What is the diamond problem in inheritance?

The "diamond problem" (sometimes referred to as the "Deadly Diamond of Death") is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C.

What is Diamond problem in inheritance in C++?

The diamond problem The diamond problem occurs when two superclasses of a class have a common base class. For example, in the following diagram, the TA class gets two copies of all attributes of Person class, this causes ambiguities.

How do you avoid the diamond problem in multiple inheritance?

Solution of the Diamond Problem: The solution is to use of the keyword virtual on the two parent classes ClassA and ClassB. Two-parent classes with a common base class will now inherit the base class virtually and avoid the occurrence of copies of the base class in the child class (ClassC here).


2 Answers

Since you say the below code, which works on the implementations I have, is broken for you then obviously the code isn't the problem. The problem is with something else in your set up; perhaps a compiler bug. You should narrow down what else could be the cause of the problem; since the code itself is ruled out as a problem perhaps the best next step is to update your compiler.

In any case that makes this question rather specific to your set up. If you do find a solution that might apply to other people then you should come back and post it. Until then I'm voting to close this question.


I'm trying to reproduce your issue. Here's the code I'm using:

#include <iostream>

struct A { int a; };
struct B { int b; };
struct B2 : virtual B, virtual A {};
struct B3 : virtual B, virtual A {};
struct B4 : virtual B2, virtual B3 {}; // these virtuals are unnecessary in this case...
struct C : virtual A {};
struct D : B4, C {};

int main() {
    D d;
    std::cout << &((B4*)&d)->a << '\n';
    std::cout << &((B3*)(B4*)&d)->a << '\n';
    std::cout << &((B2*)(B4*)&d)->a << '\n';
    std::cout << &((A*)(B2*)(B4*)&d)->a << '\n';
    std::cout << &((A*)(B3*)(B4*)&d)->a << '\n';
    std::cout << &((C*)&d)->a << '\n';
    std::cout << &((A*)(C*)&d)->a << '\n';
}

But the results I get are as expected, where the a member is the same for every object. I get the same results if I use print the addresses out in the constructors as well: http://ideone.com/8FdQ1O

If I make a slight change and remove the virtual keyword from C's definition:

...
struct C : A {};
...

(version using constructors)

then I do see the problem you describe where C has it's own A sub-object different from the virtual one used by B2, B3, and B4.

Are you sure you're using the virtual keyword in all the places you need it? The results you show seem to indicate you're missing it somewhere. Also I note that the output you show does not reflect the same order of constructors as the code fragments you show; the output shows A() first, but the code indicates that B() should be executed first.


The way virtual inheritance works is that a most derived type will contain a single virtual sub-object for each type that is virtually inherited anywhere in the inheritance tree. Additionally the most derived type will contain a sub-object for each instance of non-virtual inheritance:

struct A {};
struct B : virtual A {};
struct C : A, B {};
struct D : virtual A, C {};
struct E : A, D {};
struct F : virtual A, E {};
struct G : A, F {};

G g;

g contains a total of four A sub objects; one for each time A is non-virtually inherited (in C, E, and G), and once for all of the times A is virtually inherited (in B, D, and F).

like image 87
bames53 Avatar answered Sep 30 '22 09:09

bames53


What code do you currently have? It looks like the solution is going to be:

class D;
class C : public virtual D;
class B4 : public virtual D;
class B2 : public virtual B4;
class B3 : public virtual B4;
class B : public B2, public B3;
class A : public B2, public B3, public C;

based on your diagram. If I'm reading it wrong and A is the base, not D. Then it would need to look like this:

class A;
class B;
class B2 : public virtual B, public virtual A;
class B3 : public virtual B, public virtual A;
class C : public virtual A;
class B4 : public virtual B2, public virtual B3;
class D : public B4, public C;
like image 37
OmnipotentEntity Avatar answered Sep 30 '22 08:09

OmnipotentEntity