为什么这个钻石类继承输出不是我所期望的?

时间:2017-04-24 06:29:05

标签: c++ inheritance

考虑:

#include <iostream>

using namespace std;

class A {// base class
private:
    int data;
public:
    A(int data = 0) 
    {
        this->data = data;
    }
    void show() 
    {
        cout << data << endl;
        return;
    }
};

class B : virtual public A {
public:
    B(int data = 0) :
        A(data) {
    }
};

class C : virtual public A {
public:
    C(int data = 0) :
        A(data) {
    }
};

class D : public B, public C {
public:
    D(int dataB = 0, int dataC = 0) :
        B(dataB),
        C(dataC) {
    }
};

int main() {
    D d(1, 2);
    d.B::show();
    d.C::show();
    return 0;
}

上面的代码是钻石类继承图。基类是A.我使用虚拟继承来避免钻石问题。但是为什么这个程序的输出 0,0 ,而不是 1,2 ,如我所料?

B的构造函数已通过data=1,并在其初始化列表中使用A调用dataC类似的构造函数已通过data=2及其初始化列表,并A调用data

然后,我们会向BC子对象询问show的值。我们得到0 0而不是1 2

3 个答案:

答案 0 :(得分:22)

当你有这个带有虚拟继承的方案时,由层次结构中最派生的类(在这种情况下为.... RUN mix deps.get # Add project sources COPY . . RUN mix deps.compile ... )来调用公共基础的构造函数(D 1,2

由于A的构造函数具有默认参数A,因此可以将其用作默认构造函数。这就是正在发生的事情,公共data = 0子对象获取默认构造,因为您从A的成员初始化列表中省略了它。

如果你删除D的默认值,你会得到一个很好的编译器错误强调:

data

On g++ it results with

A(int data) 
{
    this->data = data;
}

1 请记住,对于虚拟继承,只有一个类型为main.cpp: In constructor 'D::D(int, int)': main.cpp:37:16: error: no matching function for call to 'A::A()' C(dataC) { 的子对象。 AB子对象都引用它。您致C的电话不可能打印不同的内容,因为他们访问相同的数据。这就是为什么它取决于最派生的类,所以没有歧义。

[class.mi/4]

  

不包含关键字virtual的基类说明符指定非虚基类。包含关键字virtual的基类说明符指定虚拟基类。对于最派生类的类网格中非虚拟基类的每个不同出现,最派生的对象应包含该类型的对应的不同基类子对象。 对于指定为virtual的每个不同的基类,最派生的对象应包含该类型的单个基类子对象。

[class.base.init/13.1]

  

在非委托构造函数中,初始化按以下顺序进行:

     
      
  • 首先,并且仅针对派生程度最高的类的构造函数,虚拟基类按照它们在深度优先从左到右的遍历中出现的顺序进行初始化基类的非循环图,其中“从左到右”是派生类base-specifier-list中基类出现的顺序。

  •   
  • ...

  •   

2 因此,如果您想构建具有特定数据的show,请改为定义A,而不是:

D::D()

答案 1 :(得分:7)

当你有virtual继承时,virtual基类由最派生类的构造函数初始化。

D(int dataB = 0, int dataC = 0) :
    B(dataB),
    C(dataC) {}

相当于:

D(int dataB = 0, int dataC = 0) :
    A(),
    B(dataB),
    C(dataC) {}

在您的情况下,与

相同
D(int dataB = 0, int dataC = 0) :
    A(0),
    B(dataB),
    C(dataC) {}

除非您构建B的实例,

B(int data = 0) :
    A(data) {
}

相同
B(int data = 0) {}

没有用于初始化A的代码,因为A的{​​{1}}已在D的构造函数中初始化。

同样的事情适用于C::C(int data)

的实施

这解释了你所看到的输出。

答案 2 :(得分:0)

我认为你误解了virtual继承的概念和钻石问题&#39;。使用virtual继承,您可以创建一个钻石继承模式,但是从您的帖子中,您似乎想要避免这种情况,而是有两个基础A,一个来自B,另一个来自C {1}}。要获得这一点,只需在代码编写BC1 2中避免虚拟继承。

很明显,如果只有B virtual继承A C A来自D的传统继承,那么A将有两个基础B,但来自Check dependencies Failed to create provisioning profile. There are no devices registered in your account on the developer website. Plug in and select a device to have Xcode register it. No profiles for 'net.package.my' were found: Xcode couldn't find a provisioning profile matching 'net.package.my'. Code signing is required for product type 'Application' in SDK 'iOS 10.2' 的内容再次默认初始化(如其他答案中所述)。