CluelessButCurious
CluelessButCurious

Reputation: 153

c++: constructor in initializer list order

I have couple question regarding how the following code runs

using namespace std;
class A
{
    int i;
public:
    A() { i = 7; cout << 1 << f() << i; }
    A(int i) :i(i) { cout << 1 << f() << i; }
    char f() { return 'A'; }
};
class B : public virtual A
{
    int i;
public:
    B(int i) : A(i), i(++i) { cout << 2 << i; }
    virtual char f() { return 'B'; }
};
class C : public virtual A {
public:
    C(int i) : A(i) { cout << 3 << i; }
    virtual char f() { return 'C'; }
};
class D :public A {
public:
    D(int i) { cout << 4 << i; }
    virtual char f() { return 'D'; }
};
class E : public B, public C, public D {
public:
    E() : B(2), C(3), D(4) { cout << 5; }
    virtual char f() { return 'E'; }
};
int main()
{
    E e;
    return 0;
}

So the output should be '1A723331A7445'.

Upvotes: 2

Views: 61

Answers (1)

Right, so let me start off by saying you've created a truly messed up type hierarchy. If you were trying to understand initialization order, this example is only likely to confuse you.

Anyway, to make things clearer, I modified your code and added a slash / character to the end of each c'tor print statement. So we can more easily discern what part of the line belongs to each c'tor. This gives the following output:

1A7/23/33/1A7/44/5

Before I get to the initialization order, you should know that all the virtual functions you specified won't by dynamically dispatched. A virtual function in the c'tor body will be statically bound. So for our intents and purposes, you don't really have virtual functions called in your code.

Now, to quote the C++ standard, this is how the initialization order will be determined ([class.base.init]/13):

In a non-delegating constructor, initialization proceeds in the following order:

  • First, and only for the constructor of the most derived class, virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

  • Finally, the compound-statement of the constructor body is executed.

So let's break your initialization apart:

1) The virtual A sub-object is default constructed, since you didn't specify it in the member initializer list of E(), it executes A() for the object that is shared for B and C, and prints 1A7/.

2) Now the c'tor for B is called, executing B(int i) with i = 2. It sets B::i to 3, and the c'tor body prints 23/.

3) C is constructed by calling C(int i) with i = 3. This prints 33/.

4) Now it's time to construct D. So you call D(int i) with i = 4. Since D inherits from A non-virtually, it will have a distinct A sub-object that needs construction now.

  1. You again didn't specify a parameter for it in the member initializer list, so A is default constructed. This prints 1A7/.

  2. Now the body of D(int i) runs, and prints 44/.

5) Finally, the body of E() is called, and prints 5.

Upvotes: 1

Related Questions