Order of Constructor Call in Virtual Inheritance

virtual inheritance constructor order

In your example, your output is to be expected. Virtual inheritance comes into play in the instance when you have a class with multiple inheritance who's parent classes also inherit from the same class/type (i.e. the "diamond problem"). In your example, your classes might be set up to virtually inherit (if needed elsewhere in the code), but they don't necessarily 'virtually inherit' based on your example since none of the derived classes (B1/B2/C1/C2) do more than inherit directly from A.

To expand, I've tweaked your example to explain a little more:

#include <cstdio>

#define tracefunc printf(__FUNCTION__); printf("\r\n")
struct A
{
A() { tracefunc; }
virtual void write() { tracefunc; }
virtual void read() { tracefunc; }
};

struct B1 : public A
{
B1() { tracefunc; };
void read(){ tracefunc; }
};

struct C1 : public A
{
C1() { tracefunc; };
void write(){ tracefunc; }
};

struct B2 : virtual public A
{
B2() { tracefunc; };
void read(){ tracefunc; }
};

struct C2 : virtual public A
{
C2() { tracefunc; };
void write(){ tracefunc; }
};

// Z1 inherits from B1 and C1, both of which inherit from A; when a call is made to any
// of the base function (i.e. A::read or A::write) from the derived class, the call is
// ambiguous since B1 and C1 both have a 'copy' (i.e. vtable) for the A parent class.
struct Z1 : public B1, public C1
{
Z1() { tracefunc; }
};

// Z2 inherits from B2 and C2, both of which inherit from A virtually; note that Z2 doesn't
// need to inherit virtually from B2 or C2. Since B2 and C2 both virtual inherit from A, when
// they are constructed, only 1 copy of the base A class is made and the vtable pointer info
// is "shared" between the 2 base objects (B2 and C2), and the calls are no longer ambiguous
struct Z2 : public B2, public C2
{
Z2() { tracefunc; }
};

int _tmain(int argc, _TCHAR* argv[])
{
// gets 2 "copies" of the 'A' base since 'B1' and 'C1' don't virtually inherit from 'A'
Z1 z1;
// gets only 1 "copy" of 'A' base since 'B2' and 'C2' virtualy inherit from 'A' and thus "share" the vtable pointer to the 'A' base
Z2 z2;

z1.write(); // ambiguous call to write (which one is it .. B1::write() (since B1 inherits from A) or A::write() ?)
z1.read(); // ambiguous call to read (which one is it .. C1::read() (since C1 inherits from A) or A::read() ?)

z2.write(); // not ambiguous: z2.write() calls C2::write() since it's "virtually mapped" to/from A::write()
z2.read(); // not ambiguous: z2.read() calls B2::read() since it's "virtually mapped" to/from A::read()
return 0;
}

While it might be "obvious" to us humans which call we intend to make in the case of the z1 variable, since B1 doesn't have a write method, I would "expect" the compiler to choose the C1::write method, but due to how the memory mapping of objects work, it presents a problem since the base copy of A in the C1 object might have different information (pointers/references/handles) than the copy of the A base in the B1 object (since there's technically 2 copies of the A base); thus a call to B1::read() { this->write(); } could give unexpected behaviour (though not undefined).

The virtual keyword on a base class specifier makes it explicit that other classes that virtually inherit from the same base type, shall only get 1 copy of the base type.

Note that the above code should fail to compile with compiler errors explaining the ambiguous calls for the z1 object. If you comment out the z1.write(); and z1.read(); lines the output (for me at least) is the following:

A::A
B1::B1
A::A
C1::C1
Z1::Z1
A::A
B2::B2
C2::C2
Z2::Z2
C2::write
B2::read

Note the 2 calls to the A ctor (A::A) before Z1 is constructed, while Z2 only has 1 call to the A constructor.

I recommend reading the following on virtual inheritance as it goes more in depth on some of the other pitfalls to take note of (like the fact that virtually inherited classes need to use the initialization list to make base class ctor calls, or that you should avoid using C-style casts when doing such a type of inheritance).

It also explains a little more to what you were initially alluding to with the constructor/destructor ordering, and more specifically how the ordering is done when using multiple virtual inheritance.

Hope that can help clear things up a bit.

Forced to call the base constructor when using virtual inheritance although it will never be called?

In virtual inheritance, the most-derived class has to directly call all of its ancestor constructors. Since Base doesn't have a default constructor, Middle1() and Middle2() can't compile if they can't pass an int& to Base().

However, in the code you have shown, there is no reason for Base() to take an int by reference. Pass it by value instead, and then Middle1() and Middle2() can pass 0 to Base():

class Base
{
private:
int value;
protected:
Base(int value = 0) { this->value = value; }
};

class Middle1 : virtual public Base
{
protected:
Middle1() { }
};

class Middle2 : virtual public Base
{
protected:
Middle2() { }
};

class Foo : public Middle1, public Middle2
{
public:
Foo(int value) : Base(value) { }
};

Though, I would suggest passing a pointer (or a std::optional) instead:

class Base
{
private:
int value;
protected:
Base(int* avalue = nullptr) { if (avalue) this->value = *avalue; }
};

class Middle1 : virtual public Base
{
protected:
Middle1() { }
};

class Middle2 : virtual public Base
{
protected:
Middle2() { }
};

class Foo : public Middle1, public Middle2
{
public:
Foo(int& value) : Base(&value) { }
};

C++ Virtual inheritance and constructors

There are two big keys here:

  1. Virtual base classes are initialized directly by the most derived class constructor, and not indirectly by other base classes.

  2. Virtual base classes are initialized before any non-virtual base classes.

So let's look at your examples.

When setting only B:A to virtual I get:

A() B(2) A(3) C(3) D()

That makes some sense - B inherits A virtually, and therefore, unless specifically stated otherwise, B(int x) calls A's default constructor.

B does not call A's constructor at all. D calls A directly for the virtual instance of A. But since the constructor of D doesn't specify how to initialize A, you get A's default constructor.

The A in B(int i) : A(i) gets ignored not because A is a virtual base, but because B is not the most derived class and so doesn't get to construct its A.

When setting only C:A to virtual I get:

A() A(2) B(2) C(3) D()

Why do A's constructors both precede B's and C's? That behavior makes no sense to me.

Here C::A is the only virtual base class, so it gets initialized first. Again, since the constructor of D doesn't specify an initializer for A, that virtual subobject uses the default constructor.

Then the non-virtual base classes go in order. B comes before C, but B first needs to initialize its non-virtual A (with 2).

When setting only D:C to virtual I get:

A(3) C(3) A(2) B(2) D()

Why would C's constructor precede B's?

The only virtual base class is C, so it gets constructed before B. But this time the constructor of C must first initialize its non-virtual base subobject C::A. Then comes B, which must first initialize its non-virtual subobject B::A.

Finally, there's the "normal" pattern, which makes both A inheritances virtual, resulting in only one A subobject, which is the entire point of virtual inheritance. It's pointless to make B or C virtual unless you expect D to be reused as a base class in an even more complicated way.

#include <iostream>
using namespace std;

struct A{
A() { cout << "A()" << endl; }
A(int i) { cout << "A(" << i << ")" << endl; }
};

struct B : virtual public A{
B(int i) : A(i){ cout << "B(" << i << ")" << endl; }
};

struct C : virtual public A{
C(int i) : A(i){ cout << "C(" << i << ")" << endl; }
};

struct D : public B, public C{
D() : A(1), B(2), C(3){ cout << "D()" << endl; }
};

void main() {
D d;
system("pause");
}

Output:

A(1) B(2) C(3) D()

Note I've added an initializer A(1) to D, to show that a virtual subobject does not necessarily need to use the default constructor. You could have done this in any of your examples where at least one A inheritance is virtual.

C++ constructor order while virtual inheritance

Since Birds uses virtual inheritance with Animal, then any derivation of Birds is also using virtual inheritance with Animal. In particular:

class Flamingo : public Birds { /* ... */ };

is implicitly equivalent to:

class Flamingo : virtual Animal, public Birds { /* ... */ };

And if you had written it explicitly, then you would have expected to add code to Flamingo to call the constructor on Animal (or allow Flamingo to implicitly invoke Animal's default constructor). Moreover, Flamingo's initialization of the Animal instance overrides Birds'.

So, the initialization is still AnimalBirdsFlamingo, but the Animal initialization is whatever Flamingo does, and Birds' initialization is skipped, since Animal is already initialized.

Why is Default constructor called in virtual inheritance?

When using virtual inheritance, the virtual base class's constructor is called directly by the most derived class's constructor. In this case, the daughter constructor directly calls the grandmother constructor.

Since you didn't explicitly call grandmother constructor in the initialization list, the default constructor will be called. To call the correct constructor, change it to:

daugther(int attr) : grandmother(attr), mother(attr) { ... }

See also This FAQ entry.

Constructor order in virtual inheritance

The direct inheritance of Class by Final and Base is not virtual, so an instance of Final has two base class subobjects of type Class. The one that is the direct base of Base is constructed before Base, and the one that is the direct base of Final is constructed afterwards (in fact after MI).

The reason is that:

  1. direct bases are constructed in the order they're listed (unless they're a virtual base that has been constructed already),
  2. bases are constructed before the class's own constructor runs.

Applying (1) to Final tells us that Class is constructed after MI. Applying (2) several times tells us that Class is constructed before Base, before Derived1 and Derived2, before MI.



Related Topics



Leave a reply



Submit