0
votes

I know there is clear answer for this question: Base class constructor is called first, then derived class constructor is called.

But I don't fully understand the word "called". Does it mean the beginning of usage of the constructor, or the completion of usage of the constructor? In other word, there are two possible orders for the code below:

  1. BaseClass constructor starts -> BaseClass constructor completes -> DerivedClass constructor starts -> DerivedClass constructor completes.

  2. DerivedClass constructor starts -> BaseClass constructor starts -> BaseClass constructor completes -> DerivedClass constructor completes.

Which one should be the correct order? And if 1 is correct, how does the compiler know to call BaseClass constructor before we initialize a DerivedClass instance?

It seems case 2 is correct: "called" should mean the completion of constructor. A follow-up question is how about the destructor? I know the standard answer is "the destructor of derived class is called first". So which is the correct order for:

  • DerivedClass destructor starts
  • DerivedClass destructor completes
  • BaseClass destructor starts
  • BaseClass destructor completes

Thanks

class BaseClass {
public:
    BaseClass() {
        cout << "BaseClass constructor." << endl;
    }
};

class DerivedClass : public BaseClass {
public:
    DerivedClass() : BaseClass() {
        cout << "DerivedClass constructor." << endl;
    }
};

int main() {
    DerivedClass dc;
}
2
Why don't you run the program and find out?Neil Kirk
Try It And See, an important motto ;).Cory Nelson
If 2 was correct, exactly where in the derived class constructor would the base class constructor be "injected"? How would the compiler make that decision?Tomas Aschan
That is actually an interesting question: Since the derived class' constructor could be defined in some translation unit A and an object of that type constructed in another B, the compiler won't know in B which base class ctor to call if there are several. That is, some parts of the derived class' ctor have to be used prior to calling a base class ctor.dyp
Run the code and see the order of messages.Neil Kirk

2 Answers

5
votes

[class.base.init]/10, emphasis mine:

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 [...]

  • 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.

That is, the derived class' ctor is "called" first, but before its compound-statement (its function body), the base class ctor must complete.


One way we can see this order is to use a function-try-block in the ctor of the derived class:

#include <iostream>

struct Base {
    Base() { throw "Base throwing\n"; }
};

struct Derived : Base{
    Derived()
    try : Base()
    {}
    catch(char const* p) {
        std::cout << p;
    }
};

int main() {
    try { Derived d; }catch(...){}
}

Live example

The function-try-block can catch exceptions that occur during the initialization of bases and members. The exception is propagated implicitly: since the base/member could not be constructed/initialized, the (derived) object cannot be constructed/initialized.


For the destructor, [class.dtor]/8

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class, its destructor calls the destructors for X’s virtual base classes.

All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor.

If the destructor is virtual, the compiler (sometimes) cannot know which destructors (most-derived type and its bases) have to be called at the destruction site/translation unit. Therefore, the destructor itself has to invoke the destruction of bases and members (at least, for virtual destructors called via dynamic dispatch).

0
votes

The correct answer is choice 1, but this can be easily verified by actually running the code you already typed out