Order of Calling Base Class Constructor from Derived Class Initialization List

Order of calling base class constructor from derived class initialization list

The order you refer in your question is not the "order of calling base constructor". In fact, you can't call a constructor. Constructors are not callable by the user. Only compiler can call constructors.

What you can do is to specify initializers. In this case (constructor initializer list) you are specifying initializers for subobjects of some larger object. The order in which you specify these initializers does not matter: the compiler will call the constructors in a very specific order defined by the language specification, regardless of the order in which you specify the initializers. The base class constructors are always called first (in the order in which the base classes are listed in class definition), then the constructors of member subobjects are called (again, in the order in which these members are listed in the class definition).

(There are some peculiarities in this rule when it comes to virtual base classes, but I decided not to include them here.)

As for the bad behaviors... Of course there is a potential for "bad behaviors" here. If you assume that the order of initialization depends on the order you used in the constructor initializer list, you will most likely eventually run into an unpleasant surprise, when you discover that the compiler completely ignores that order and uses its own order (the order of declaration) instead. For example, the author of this code

struct S {
int b, a;
S() : a(5), b(a) {}
};

might expect a to be initialized first, and b to receive the initial value of 5 from a, but in reality this won't happen since b is initialized before a.

Order of initialization in derived classes

The base class constructor is called before any member constructors in the derived class. This should work fine:

Child(string messg_arg) : Parent(messg_arg), message(messg_arg)
{}

What are the rules for calling the base class constructor?

Base class constructors are automatically called for you if they have no argument. If you want to call a superclass constructor with an argument, you must use the subclass's constructor initialization list. Unlike Java, C++ supports multiple inheritance (for better or worse), so the base class must be referred to by name, rather than "super()".

class SuperClass
{
public:

SuperClass(int foo)
{
// do something with foo
}
};

class SubClass : public SuperClass
{
public:

SubClass(int foo, int bar)
: SuperClass(foo) // Call the superclass constructor in the subclass' initialization list.
{
// do something with bar
}
};

More info on the constructor's initialization list here and here.

How do I call the base class constructor?

You do this in the initializer-list of the constructor of the subclass.

class Foo : public BaseClass {
public:
Foo() : BaseClass("asdf") {}
};

Base-class constructors that take arguments have to be called there before any members are initialized.

The call sequence of the Constructors between Derived Class and base class

If you write:

B(int i) { cout<<"B con with param"<<endl; }

then constructor A() (without arguments) will be called.

If you write:

B(int i): A(i) { cout<<"B con with param"<<endl; }

then constructor A(int) will be called.

Order of member variables initialization of the base class in the derived class (constructor)

Before I explain the problem, let me say this: if your object's initialization is so complex that it requires the detailed knowledge I'm about to relate, then fix that. Overcomplicating your initialization is to nobody's benefit.

Now, let's look at what happens when you construct an object of type B by calling its default constructor.

Since B::A is not mentioned in the member initializer list, it will be default initialized. That will call A(int) with its default value of 0. The result of this will be initializing B::A::m_i with the value 0, followed by outputing that value, followed by incrementing B::A::m_i to the value 1.

Now, we initialize the members of B. The first member to be initialized is B::m_x. The member initializer for this reads and increments the value of B::A::m_i, which is a validly initialized value that happens to be 1 before this operation. So before calling A(int) for B::m_x, B::A::m_i takes the value 2.

B::m_x is initialized via a call to A(int) with the value 2. It stores that value in B::m_x.m_i, outputs it, and increments it by one.

Next, we initialize B::m_a. The member initializer initializes it via a call to new A[2], which will default construct 2 instances of A. These are all completely irrelevant to your question, as they are in no way related to B::A::m_i. But it does output two zeros.

After initializing B::m_a, we enter B's default constructor itself.There, it simply outputs the value of B::A::m_i. Which if you recall, was last set to 2. So that's what it outputs.



Related Topics



Leave a reply



Submit