【问题标题】:Complex diamond issue: C++ virtual inheritance复杂的钻石问题:C++ 虚拟继承
【发布时间】:2012-11-05 13:22:49
【问题描述】:

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

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

【问题讨论】:

  • 那是一颗丑陋的钻石。
  • 您需要提供您正在使用的实际继承关系。考虑在单个答案中使用格式(只需提供'derived_type : [virtual] base, [virtual] base2 ...')。解释一下你是如何生成输出的也会很有趣
  • 继承真的是你想要(需要)的吗?有时答案是否定的。您还有其他选择继承。考虑一下!
  • “位置指针”的值是如何确定的?
  • 位置指针只是A类的受保护成员。我使用了一个空的构造函数。

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


【解决方案1】:

既然你说下面的代码,它适用于我的实现,对你来说是坏的,那么显然代码不是问题。问题在于您的设置中的其他内容;也许是编译器错误。您应该缩小可能导致问题的其他原因;由于代码本身被排除为问题,也许最好的下一步是更新您的编译器。

无论如何,这使得这个问题对您的设置相当具体。如果您确实找到了可能适用于其他人的解决方案,那么您应该回来发布它。在此之前,我将投票结束这个问题。


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

#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 有自己的 A 子对象,与 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)。

【讨论】:

  • 不幸的是,我添加了所有需要的虚拟关键字,但我仍然得到了这个错误。我用的是 4.6.3
  • @GuillaumeRacicot 你是说当你运行我提供的程序时你看到了问题吗?还是您以某种方式修改了代码?您必须提供足够的实际代码,以便任何人诊断问题,因为已经提供的片段似乎不包含问题的原因。
  • 你发给我的程序是一模一样的。问题是我的代码与工作代码相同...
  • 如果采用我的确切代码(甚至没有以不同的分号放置)代码,在 ideone.com 上的 gcc 4.5 或我有权访问的任何其他编译器上运行时不会显示问题,并且它在您运行它时显示问题,那么问题似乎是您的特定设置的错误。也许这是 gcc 4.6.3 中的一个错误。您必须进行试验才能找到解决方法。我不知道任何解决方法,也无法进行实验,因为我一开始就无法重现该问题。
【解决方案2】:

为什么 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 我认为您应该重新考虑您的等级制度。我不知道细节,但似乎您的问题通过组件设计有一个更清洁的解决方案。

【讨论】:

  • 不,我只有一个 A 的构造函数,而且 Position 的第二个指针似乎没有初始化
  • 即使B4C 是从非虚拟继承的,继承对象也会为它们共享一个A,因为它们都对A 使用虚拟继承。 D 如何继承并不重要。此外,无需显式调用 D 的每个构造函数。
【解决方案3】:

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

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;

【讨论】:

  • 哦!那么让我为 A 重写。
  • 为了更明显,他们在第二组中唯一缺少的 virtuals 是 D 上的那些。
猜你喜欢
  • 1970-01-01
  • 2017-12-09
  • 2020-01-01
  • 1970-01-01
  • 2014-05-14
  • 2012-04-19
  • 2019-10-09
  • 2020-10-03
  • 1970-01-01
相关资源
最近更新 更多