调用虚拟基类的重载构造函数
有没有一种(实用的)方法可以绕过正常的(虚拟)构造函数调用顺序?
示例:
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 是其中之一)。 [/编辑]
[编辑]
虚拟基类在初始化任何非虚拟基类之前初始化,因此只有最底层的派生类才能初始化虚拟基类。 – 詹姆斯·麦克内利斯
这正是重点,我不希望最派生的类调用虚拟基类构造函数。 [/edit]
考虑以下情况(未在上面的代码示例中表示):
A
/ \
B0 B1
\ /
C
|
D
我理解为什么在实例化 C 时 C 必须调用 A 的 ctor(歧义) ,但是为什么D在实例化D的时候必须要调用它呢?
Is there a (practical) way to by-pass the normal (virtual) constructor calling order?
Example:
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;
}
Output:
calling A()
calling B(int)
calling C(int)
calling D(int)
What I want to have is something like:
calling A(int)
calling B(int)
calling C(int)
calling D(int)
As you see, there is virtual inheritance involved, which leads the constructor of D to call the constructor of A first, but since no parameter is provided, it calls A(). There's the const int i that needs initialisation, so I've got a problem.
What I'd like to do is to hide the inheritance details of C, that's why I'm looking for a way to avoid calling A(i) in D's (and every derived) constructor's initialisation list. [edit] In this specific case, I can assume there are only non-virtual single-inheritance child classes of C (as D is one). [/edit]
[edit]
Virtual base classes are initialized before any non-virtual base classes are initialized, so only the most derived class can initialize virtual base classes. – James McNellis
That's exactly the point, I don't want the most derived class to call the virtual base class constructor.
[/edit]
Consider the following situation (not represented in the code example above):
A
/ \
B0 B1
\ /
C
|
D
I understand why C has to call the ctor of A (ambiguity) when you instantiate C, but why does D have to call it when instantiating D?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不幸的是,您始终必须从最底层的派生类调用虚拟基类构造函数。
这是因为您说虚拟基在从该对象实例派生的所有类之间共享。由于对于给定的对象实例化只能调用一次构造函数,因此您必须在最派生的类中显式调用构造函数,因为编译器不知道有多少类共享虚拟基(从 The 解释(可能很糟糕)) C++ 编程语言第 3 版,第 15.2.4.1 节)。这是因为编译器将从最基类的构造函数开始,一直到最底层的派生类。根据标准,直接从虚拟基类继承的类不会调用其虚拟基类构造函数,因此必须显式调用它。
Unfortunately, you will always have to call the virtual base classes constructor from the most derived class.
This is because you are saying that the virtual base is shared between all classes that derive from it for the instance of the object. Since a constructor may only be called once for a given instaniation of an object, you have to explicitly call the constructor in the most derived class because the compiler doesn't know how many classes share the virtual base (paraphrased (probably poorly) from The C++ Programming Language 3rd edition, section 15.2.4.1). This is because the compiler will start from the most base class's constructor and work to the most derived class. Classes that inherit from a virtual base class directly, will not, by the standard, call their virtual base classes constructor, so it must be called explicitly.
出于同样的原因,C 必须调用它。这不是歧义问题,而是 A 的构造函数只能被调用一次(因为它是虚拟基)。
如果您希望 C 能够初始化 A 的构造函数,那么如果类 D 继承 C 而另一个类最终继承 A 呢?
For the same reason that C has to call it. It's not an issue of ambiguity, it's the fact that A's constructior must be called only once (since it's a virtual base).
If you were hoping that C might be able to initialise A's constructor then what if class D were to inherit C and another class that ultimately inherits A?
这就是规则。有重写虚拟函数的规则和构造虚拟基础子对象的规则。尽管两者在概念上非常相似,但它们遵循完全不同的规则,原因是:重写虚函数是显式的。对于默认构造函数来说,调用构造函数是隐式的。
虚拟基类中的虚拟函数只需要有一个最终重写器,即重写所有其他重写器的重写器。 (非虚拟基类中的虚拟函数不可能有两个重写器,这样一个重写器就不会重写另一个。)
但是虚拟基类构造函数总是从最派生的类中调用,并且通常以隐式形式调用,无需提及ctor-init-list 中的虚拟基类,因为大多数设计用作虚拟基类的类都是“纯接口”,没有数据成员,也没有用户初始化。
Such are the rules. There are rules for overriding virtual functions and rules for constructing virtual base subobjects. Although both are very similar conceptually, they follow completely different rules, for a reason: overriding a virtual function is explicit. Calling a constructor is implicit for the default constructor.
Virtual functions in virtual base classes are only required to have one final overrider, an overrider that overrides all others overriders. (Virtual functions in non-virtual base classes cannot possibly have two overriders such that one does not override the other.)
But virtual base class constructors are always called from the most derived class, and usually in the implicit form of not bothering to mention the virtual base class in the ctor-init-list, as most classes designed to be used as virtual base classes are "pure interfaces" with no data members and no user initialization.
在 parashift c++-faq-lite 上,这个问题概述了< /a>.
On parashift c++-faq-lite this issue is outlined.