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)" &l开发者_JS百科t;< 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?
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.
I understand why C has to call the ctor of A (ambiguity) when you instanciate C, but why does D have to call it when instanciating D?
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?
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.
On parashift c++-faq-lite this issue is outlined.
精彩评论