是否有(实用的)绕过正常(虚拟)构造函数调用顺序的方法?
示例:
class A
{
const int i;
public:
A()
: i(0)
{ cout << "calling A()" << endl; }
A(int p)
: i(p)
{ cout << "calling A(int)" << endl; }
};
class B
: public virtual A
{
public:
B(int i)
: A(i)
{ cout << "calling B(int)" << endl; }
};
class C
: public B
{
public:
C(int i)
: A(i), B(i)
{ cout << "calling C(int)" << endl; }
};
class D
: public C
{
public:
D(int i)
: /*A(i), */ C(i)
{ cout << "calling D(int)" << endl; }
};
int main()
{
D d(42);
return 0;
}
输出:
致电A()
叫B(int)
调用C(int)
调用D(int)
我想拥有的是:
调用A(int)
叫B(int)
调用C(int)
调用D(int)
如您所见,涉及虚拟继承,这导致D的构造函数首先调用A的构造函数,但由于没有提供参数,因此它调用A()。需要初始化的是 const int i,所以我遇到了问题。
我想要隐藏C的继承细节,这就是为什么我在寻找一种方法来避免在D(和每个派生的)构造函数的初始化列表中调用A(i)。 [编辑]在这个特定的情况下,我可以假设只有非虚拟的单继承子类C(因为D是一个)。 [/编辑]
[编辑]
在初始化任何非虚拟基类之前初始化虚拟基类,因此只有最派生的类才能初始化虚拟基类。 - 詹姆斯麦克尼利斯
正是这一点,我不希望最派生的类调用虚拟基类构造函数。 的 [/编辑]
请考虑以下情况(未在上面的代码示例中表示):
A
/ \
B0 B1
\ /
C
|
D
我理解为什么C在实例化C时必须调用A(歧义)的ctor,但为什么D在实例化D时必须调用它?
答案 0 :(得分:7)
不幸的是,您将始终必须从派生程度最高的类调用虚拟基类构造函数。
这是因为你说的是虚拟基础在从它派生的所有类之间共享,用于对象的实例。由于构造函数只能在对象的给定实例中被调用一次,因此您必须在最派生类中显式调用构造函数,因为编译器不知道有多少类共享虚拟基础(从“The”中释放(可能很差)) C ++编程语言第3版,第15.2.4.1节)。这是因为编译器将从最基类的构造函数开始,并且工作到派生程度最高的类。直接从虚拟基类继承的类不会通过标准调用它们的虚基类构造函数,因此必须显式调用它。
答案 1 :(得分:2)
我理解为什么C必须打电话给 A(歧义)当你的时候 instanciate C,但为什么D必须 在实施D?
时调用它
出于同样的原因,C必须调用它。这不是歧义问题,而是A的构造必须只被调用一次(因为它是虚拟基础)。
如果您希望C能够初始化A的构造函数,那么如果D类继承C而另一个最终继承A的类会怎样?
答案 2 :(得分:0)
这是规则。存在用于覆盖虚拟功能的规则和用于构造虚拟基础子对象的规则。虽然两者在概念上非常相似,但它们遵循完全不同的规则,原因是:覆盖虚函数是显式的。默认构造函数隐式调用构造函数。
虚拟基类中的虚函数只需要有一个最终覆盖,覆盖所有其他覆盖的覆盖。 (非虚拟基类中的虚函数不可能有两个覆盖,使得一个不会覆盖另一个。)
但是虚拟基类构造函数总是从派生类最多的类中调用,并且通常以隐含的形式不打扰ctor-init-list中的虚拟基类,因为大多数类被设计为用作虚拟基础类是“纯接口”,没有数据成员,也没有用户初始化。
答案 3 :(得分:-1)
在parashift c ++ - faq-lite这个问题is outlined。