The following code won't compile:
class A {
public:
A(int) {}
};
class B: virtual public A {
public:
B(): A(0) {}
};
// most derived class
class C: public B {
public:
C() {} // wrong!!!
};
If I call A
's constructor in C
's constructor initialization list, that is:
// most derived class
class C: public B {
public:
C(): A(0) {} // OK!!!
};
it does work.
Apparently, the reason is because virtual base classes must always be constructed by the most derived classes.
I don't understand the reason behind this limitation.
Because it avoids this:
class A {
public:
A(int) {}
};
class B0: virtual public A {
public:
B0(): A(0) {}
};
class B1: virtual public A {
public:
B1(): A(1) {}
};
class C: public B0, public B1 {
public:
C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};
Because in the class hierarchy having a virtually inherited base class, the base class would/may be shared by multiple classes (in diamond inheritance for example, where the same base class is inherited by multiple classes). It means, there would be only one copy of the virtually-inherited base class. It essentially means, the base class must be constructed first. It eventually means the derived class must instantiate the given base class.
For example:
class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 // A is shared, and would have one copy only.
I find this rule error-prone and cumbersome (but then, what part of multiple inheritance isn't?).
But the logically imposed order of construction must differ from the case of normal (non-virtual) inheritance. Consider Ajay's example, minus virtual:
class A;
class B1 : A;
class B2 : A;
class C: B1,B2
In this case for each C two As are constructed, one as part of B1's construction, the other one as part of B2's construction. The code of the B classes is responsible for that, and can do it. The order of events is:
Start C ctor
Start B1 ctor
A ctor (in B's ctor code)
End B1 ctor
Start B2 ctor
A ctor (in B's ctor code)
End B2 ctor
End C ctor
Now consider virtual inheritance in
class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2
One order of event is
Start C ctor
Start B1 ctor
// NO A ctor
End B1 ctor
Start B2 ctor
// NO A ctor
End B2 ctor
A ctor // not B's code!
End C ctor
It's possible that A is constructed before B1 and B2, but that's irrelevant. The important thing is that A's construction is not happening during the construction of the other base classes. That code is simply not executed, and cannot be executed, because the virtually inherited base class sub-object of type A is part of and under the control of the most derived class which is not accessible from B1 and B2's code; indeed, the C is not even fully constructed at the point in time B1 or B2 are being constructed and could potentially attempt creating an A in a C.