复杂的钻石问题:C ++虚拟继承

时间:2012-11-16 19:19:56

标签: c++ multiple-inheritance virtual-inheritance

我有一个钻石问题,如下所示:

    __ 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 ::位置没有构造函数?我应该用什么虚拟继承来解决这个问题?感谢。

编辑:

以下是代码示例:

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

3 个答案:

答案 0 :(得分:4)

既然你说下面的代码,它对我的​​实现有效,那么你就会被打破,那么显然代码不是问题。问题出在你的设置中还有别的东西;也许是编译器错误。你应该缩小其他可能导致问题的原因;因为代码本身被排除为问题,所以最好的下一步是更新编译器。

在任何情况下,这个问题都与您的设置相关。如果您确实找到了可能适用于其他人的解决方案,那么您应该回来发布它。在那之前,我投票决定关闭这个问题。


我正在尝试重现您的问题。这是我正在使用的代码:

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

但我得到的结果与预期一致,a成员对于每个对象都是相同的。如果我在构造函数中使用打印地址,我会得到相同的结果:http://ideone.com/8FdQ1O

如果我稍作修改并从C的定义中移除virtual关键字:

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

version using constructors

然后我确实看到了你描述的问题,其中C拥有它自己的一个子对象,与B2,B3和B4使用的虚拟对象不同。

您确定在所有需要的地方使用virtual关键字吗?你展示的结果似乎表明你在某个地方错过了它。另外我注意到你显示的输出并不反映与你显示的代码片段相同的构造函数顺序;输出首先显示A(),但代码表明应首先执行B()。


虚拟继承的工作方式是,大多数派生类型将包含每个类型的单个虚拟子对象,这些子对象在继承树中的任何位置实际上都是继承的。此外,派生类型最多的类型将包含每个非虚拟继承实例的子对象:

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共包含四个A个子对象;每次A一次非虚拟地继承(CEG),并且对于所有时间A几乎是虚拟的继承(BDF)。

答案 1 :(得分: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;

答案 2 :(得分:0)

  

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

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

在D构造函数&amp; B4 :: position与&amp; C :: position

不同
  

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

不知道,你的A类有多个构造函数的可能性,以及C :: C()调用默认静默构造函数吗?

  

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

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

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