在我的代码中,我有一个基本的菱形图案:
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
答案 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值。
这就是为什么您必须在派生最多的类中初始化虚拟基类的原因。