user4165455
user4165455

Reputation:

C++ implicit and explicit inheritance constructor calls

I have a question about implicit and explicit calls to a base constructor. If we have a class hierarchy like this:

class Person{
    protected:
        std::string m_name;
    public:
        Person(std::string& _name) : m_name(_name){std::cout << "A person is being constructed." << std::endl;}
};

class Baby : public Person{
    private:
        int m_no_of_nappies;
    public:
        Baby(std::string& _name, int& _no_of_nappies) : m_no_of_nappies(_no_of_nappies), Person(_name) {std::cout << "A baby is being constructed." << std::endl ;}

};

According to my lecture notes, a call to 'Baby' in the main, like so:

std::string babyname = "Robert";
int nappies = 5;

Baby baby(babyname, nappies);

Causes the following to happen:

  1. As an explicit call to Person is made in Baby's initialisation list: Baby's initialisation list gets called, and the no_of_nappies is initialised.
  2. Next, a call to Person's constructor is made and Person's initialisation list is called. The m_name is initialised.
  3. Person's constructor body is then called.
  4. Baby's constructor body is finally called.

This makes sense, however, what about if there were implicit calls made to the default constructor of a parent class, like so:

class Vehicle{
    protected:
        int m_no_wheels;
    public:
        Vehicle() : m_no_wheels(0) { std::cout << "A vehicle is being constructed." << std::endl; }
};

class Bicycle : public Vehicle{
    protected:
        bool m_is_locked;
    public:
        Bicycle() : m_is_locked(false) { std::cout << "A bicycle is being constructed." << std::endl; }
};

This is the part that I'm not so sure about. My best guess is that a call to Bicycle bike; in the main has the following effect:

  1. An implicit call is made to Vehicle's default constructor from Bike. Before the Bike's initialisation list is called.
  2. As vehicle doesn't inherit from anything, Vehicle's initialisation list is called where it initialises m_no_wheels to 0.
  3. Vehicle's constructor body is called.
  4. We return to Bicycle and now its initialisation list is called, initialising m_is_locked to false.
  5. Bike's constructor body is called.

Could somebody please explain if my reasoning behind the implicit call is correct?

The main difference, in my opinion, is the fact that with an explicit reference to the base constructor, the child class' initialisation list is always hit first in order to call that base constructor - however, with an implicit call, the top most parent's initialisation list is always hit first.

Thank you, and much appreciated!

Edit: I'm asking specifically if the order changes, depending on an implicit or explicit call to the parent class.

Upvotes: 4

Views: 3658

Answers (2)

Marco A.
Marco A.

Reputation: 43662

§12.6.2 defines how things are initialized:

The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:

  • If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
  • Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
  • Then, non-static data members are initialized in order of declaration in the class definition.
  • Finally, the body of the constructor is executed (Note: if initialization order was controlled by the appearance in the member initializer lists of different constructors, then the destructor wouldn't be able to ensure that the order of destruction is the reverse of the order of construction)

Summarized for your case (and leaving aside virtual functions):

  1. Base classes in the order of declared inheritance
  2. Members in the order of declaration

Thus the order in the constructor initializer list has no effect.

In the first case you're wrong in this point: Person is a base class of Baby and gets initialized before m_no_of_nappies


Edit: Your question

Baby calls Person from within its initialisation list, hence the first thing that's hit is Baby's initialisation list?

[class.base.init]/10 is probably what you're looking for: you don't really "call" the base class constructor (assuming there's no delegation), it is called by the compiler for you when initializing the derived object.

The compiler sets things up for you to help keeping the order of constructors and destructors right

The reason for ignoring the order of initializers is to preserve the usual FIFO ordering of constructor and destructor calls. Allowing two constructors to use different orders of initialization of bases and members would constrain implementations to use more dynamic and more expensive strategies

Taken from https://stackoverflow.com/a/24287946/1938163

And finally

is the implicit call to the base class (that the compiler makes) done before the Bicycle's initialisation list or after?

Before the rest of the member class initializations as in §12.6.2.

Upvotes: 1

Brian Bi
Brian Bi

Reputation: 119632

The order of initialization of bases and members is specified in [class.base.init]/11 and you can find a summary here: http://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order

The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:

  1. If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
  2. Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
  3. Then, non-static data members are initialized in order of declaration in the class definition.
  4. Finally, the body of the constructor is executed

(Note: if initialization order was controlled by the appearance in the member initializer lists of different constructors, then the destructor wouldn't be able to ensure that the order of destruction is the reverse of the order of construction)

The order of initialization is set in stone before any constructors have been defined; the constructor initializer list only affects how bases and members are initialized, not the order in which they are initialized.

Because Person is a base of Baby, it always gets initialized before Baby's member m_no_of_nappies. As part of the initialization of Person, its own members are initialized, and then its constructor body is executed. After Person's constructor's body returns, then m_no_of_nappies is initialized. (Destruction always occurs in the reverse order.) Vehicle is likewise a base of Bicycle, and is initialized first; since there is no mem-initializer for it, the default constructor is called.

Upvotes: 2

Related Questions