完成虚拟继承
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 的原因。
在我的代码中,我有一个基本的菱形图案:
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 的原因。