完成虚拟继承

Finalise Virtual inheritance

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

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

它是这样实现的,公共基础class有一个默认构造函数和一个带参数的构造函数:

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 class 使用带参数的构造函数初始化虚拟基,一切都按预期工作。

但是,当我从 Joined class 派生出 class 时,发生了一些奇怪的事情 - CommonBase 默认构造函数被调用 除非我在派生的 classes 构造函数中显式初始化 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;
}

这段代码的输出是

======= Joined =======
Constructor value is 99
Actual value is 99
The value is 99

=== JoinedDerivedA ===
Constructor value is 99
Actual value is 0 // <-- unexpected behaviour
The value is 0

=== JoinedDerivedB ===
Constructor value is 99
Actual value is 99
The value is 99

为什么会这样? 是否可以不必在派生的 class 中再次显式初始化公共基础 class?

这是ideone上的代码,所以你可以运行自己:https://ideone.com/Ie94kb

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

(13) In a non-delegating constructor, initialization proceeds in the following order:

(13.1) — First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

(13.2) — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

(13.3) — Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

(13.4) — Finally, the compound-statement of the constructor body is executed.

[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note ]

这意味着虚拟基础 class 将在 初始化 Joined 之前 被初始化。所以在DerivedJoinedA中默认初始化为0的value。然后在初始化Joined时,忽略CommonBase的初始化,因为它已经被初始化了,value 保持其 0 值。

这就是为什么你必须在最派生的 class 中初始化虚拟基 classes 的原因。