简体   繁体   中英

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;
}

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 ).

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;

Why does D and C don't have the same pointer for position?

Because you are non-virtually inheriting D from B4 and C. That means you have two copies of A (and two pointers).

In D constructor &B4::position is different from &C::position

Why there's no constructor for this A::position?

No idea, any chances that your A class has more than one constructor, and that a default-silent constructor is called by C::C()?

What virtual inheritance should I make to solve this?

Make all virtual. That means that you need to explicitly call every constructor from D::D() (ie A::A(),B::B(),B2::B2(),B3::B3(),C::C() ).

Tbh I believe you should reconsider your hierarchy. I don't know the details, but it seems that your problem has a cleaner solution via component-design.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM