5

我有一个看起来像这样的钻石问题:

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

我尝试了很多方法来制作最好的虚拟继承来避免重复,但我找不到解决方案。A 类包含一个位置。这是一个示例输出:

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

为什么 D 和 C 没有相同的位置指针?为什么这个 A::position 没有构造函数?我应该做什么样的虚拟继承来解决这个问题?谢谢。

编辑:

这是一个代码示例:

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;

编辑2:为了输出,我把这段代码放在每个构造函数中:

A::A()
{
    std::cerr << "Call: A() position pointer is: " << &_position << std::endl;
}
4

3 回答 3

4

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

于 2012-11-16T20:38:41.653 回答
1

你目前有什么代码?看起来解决方案将是:

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;

根据你的图表。如果我读错了并且 A 是基础,而不是 D。那么它需要看起来像这样:

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;
于 2012-11-16T19:25:06.503 回答
0

为什么 D 和 C 没有相同的位置指针?

因为您从 B4 和 C 非虚拟继承 D。这意味着您有 A 的两个副本(和两个指针)。

在 D 构造函数中 &B4::position 与 &C::position 不同

为什么这个 A::position 没有构造函数?

不知道,您的 A 类是否有可能具有多个构造函数,并且 C::C() 调用了默认静默构造函数?

我应该做什么样的虚拟继承来解决这个问题?

让一切虚拟化。这意味着您需要从 D::D() 显式调用每个构造函数(即 A::A(),B::B(),B2::B2(),B3::B3(),C:: C() )。

Tbh 我相信你应该重新考虑你的等级制度。我不知道细节,但似乎您的问题通过组件设计有一个更清洁的解决方案。

于 2012-11-16T19:54:32.990 回答