Virtual inheritance and C ++ constructors

I have the following C ++ code (VS2013, if it matters):

#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 : /*virtual*/ public B, /*virtual*/ public C{
    D() : B(2), C(3){ cout << "D()" << endl; }
};

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

      

The code implements diamond-shaped inheritance, there are 4 inheritances in total: B: A, C: A, D: B, D: C

If none of these inheritances are set to virtual

, I get the following output:

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

It makes sense. D () first calls B (int x), which first calls A (int x), then calls C (int x), which first calls A (int x), and finally D () is executed.

But as far as virtual inheritance goes, I have 4 inheritances, which means I have a total of 16 virtual / non-virtual inheritance combinations.

Chatting with different parameters gives some very unexpected results, and the more I read about virtual inheritance, the more confused I become.

Here are some examples:

  • When setting only B: A on, virtual

    I get:

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

This makes sense - B inherits A practically, and therefore, unless specifically stated otherwise, B (int x) calls the default constructor.

  1. When setting only C: A on, virtual

    I get:

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

Why do constructors precede B and C? This behavior doesn't make any sense to me.

  1. When installing only D: C on virtual

    I get:

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

Why does C's constructor precede B?

I could go on and on. Some of these combinations lead to very unexpected results (at least for me).

I know the basics of virtual inheritance and understand them in simple examples, and I've seen many questions regarding this. But some of these behaviors will still puzzle me.

Is there any specific set of rules for which these multiple virtual inheritances are?

EDIT: I was redirected to this question: Mixing virtual and non-virtual inheritance of the base Whlie class , which helps a lot, I am still confused as to which of A's constructors is called under any particular set of circumstances. I still need help.

+3


source to share


1 answer


There are two big keys here:

  • Virtual base classes are initialized directly by the constructor of the derived class itself, and not indirectly by other base classes.

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

So let's take a look at your examples.

When setting only B: A to virtual, I get:

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

This makes sense - B inherits A practically, and therefore, unless specifically stated otherwise, B (int x) calls the default constructor.

B

does not call the constructor A

. D

calls A

directly for the virtual instance A

. But since the constructor D

doesn't specify how to initialize A

, you get a default constructor A

.

A

c is B(int i) : A(i)

ignored not because it A

is a virtual base, but because it is B

not the most derived class and therefore cannot build it A

.

When setting only C: A to virtual, I get:

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

Why do constructors precede B and C? This behavior doesn't make any sense to me.

Here C::A

is the only virtual base class, so it gets initialized first. Again, since the constructor D

does not specify an initializer for A

, this virtual subobject uses the default constructor.



Then non-virtual base classes go in order. B

precedes C

, but B

first needs to be initialized non-virtual A

(with 2

).

When installing only virtual machine D: C, I get:

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

Why does C's constructor precede B?

The only virtual base class C

, so it is created before B

. But this time, the constructor 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 is a "normal" pattern that makes both A

inheritances virtual, with the result that only one subobject is created A

, which is the entire virtual inheritance point. It's pointless to do B

or C

virtual unless you expect to D

be reused as a base class in an even more complex 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 added an initializer A(1)

to D

to show that the virtual subobject does not need to use the default constructor. You could do this in any of your examples where at least one inheritance A

is virtual.

+2


source







All Articles