完成虚拟继承

时间:2018-12-17 11:47:36

标签: c++ inheritance diamond-problem

在我的代码中,我有一个基本的菱形图案:

     CommonBase
        /  \
       /    \
 DerivedA  DerivedB
       \    /
        \  /
       Joined

它是这样实现的,普通的基类有一个默认的构造函数和一个带参数的构造函数:

struct CommonBase {
    CommonBase() : CommonBase(0) {}

    CommonBase(int val) : value(val) {}

    const int value;
};

struct DerivedA : public virtual CommonBase {
    void printValue() {
        std::cout << "The value is " << value << "\n";
    }
};

struct DerivedB : public virtual CommonBase {
    void printValueTimes2() {
        std::cout << "value * 2 is " << value * 2 << "\n";
    }
};

struct Joined : public DerivedA,
                public DerivedB {
    Joined(int val) : CommonBase(val) {
        std::cout << "Constructor value is " << val << "\n";
        std::cout << "Actual value is " << value << "\n";
    }
};

Joined类使用带有参数的构造函数初始化虚拟基础,一切按预期进行。

但是,当我从Joined类派生一个类时,会发生一些奇怪的事情-除非我显式初始化{{1},否则CommonBase默认构造函数会被调用。 },也可以在派生类的构造函数中使用。

使用以下代码对此进行了演示:

CommonBase

此代码的输出是

struct JoinedDerivedA : public Joined {
    JoinedDerivedA() : Joined(99) {
        printValue();
    }
};

struct JoinedDerivedB : public Joined {
    JoinedDerivedB() : Joined(99), CommonBase(99) {
        printValue();
    }
};

int main() {
    std::cout << "======= Joined =======\n";
    Joined j(99);
    j.printValue();
    std::cout << "\n=== JoinedDerivedA ===\n";
    JoinedDerivedA a;
    std::cout << "\n=== JoinedDerivedB ===\n";
    JoinedDerivedB b;

    return 0;
}

为什么会这样?是否有可能不必再次在派生类中显式初始化公共基类?

以下是ideone上的代码,因此您可以自己运行它:https://ideone.com/Ie94kb

1 个答案:

答案 0 :(得分:1)

这在初始化基础和成员[class.base.init] 中指定(n4567草案中的12.6.2)。我们可以阅读§13(强调我的观点):

  

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

     

(13.1)— 第一,并且仅对于最派生类(1.8)的构造函数,虚拟基类在其中初始化   它们在基类的有向无环图的深度优先从左到右遍历时出现的顺序,   其中“从左到右”是基类在派生类base-specifier-list中的出现顺序。

     

(13.2)— 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中   (与mem初始化程序的顺序无关)。

     

(13.3)—然后,按照在类定义中声明的顺序初始化非静态数据成员   (同样,与内存初始化程序的顺序无关)。

     

(13.4)—最后,执行构造函数主体的复合语句。

     

[注意:声明顺序是强制性的,以确保基础和成员子对象在销毁中被销毁。   初始化的相反顺序。 —尾注]

这意味着将在Joined初始化之前 初始化虚拟基类。因此,在DerivedJoinedA中,默认情况下将其初始化为value为0。然后,在初始化Joined时,CommonBase的初始化将被忽略,因为它已经被初始化并且{{ 1}}保持其0值。

这就是为什么您必须在派生最多的类中初始化虚拟基类的原因。