45

A little question about creating objects. Say I have these two classes:

struct A{
    A(){cout << "A() C-tor" << endl;}
    ~A(){cout << "~A() D-tor" << endl;}
};

struct B : public A{
    B(){cout << "B() C-tor" << endl;}
    ~B(){cout << "~B() D-tor" << endl;}

    A a;
};

and in main I create an instance of B:

int main(){
    B b;
}

Note that B derives from A and also has a field of type A.

I am trying to figure out the rules. I know that when constructing an object first calls its parent constructor, and vice versa when destructing.

What about fields (A a; in this case)? When B is created, when will it call A's constructor? I haven't defined an initialization list, is there some kind of a default list? And if there's no default list? And the same question about destructing.

Mat
  • 202,337
  • 40
  • 393
  • 406
Bug
  • 475
  • 1
  • 5
  • 4
  • 3
    Your example might be more explanatory if your message for the destructor is different from your message for the constructor. Also, what are those `std::sort` doing? – Tom Sep 24 '11 at 13:24
  • Also, when experimenting, compare the construction and destruction of `B b`, `B* b = new B(); delete b;` and `A* a = new b(); delete a;` (Compare what happens when you use the `virtual` keyword for your destructor, i.e. `virtual ~A() {cout<<"A D-tor"< – Tom Sep 24 '11 at 13:29
  • @Tom, You are right. Removing compiler errors. – iammilind Sep 24 '11 at 13:35
  • I've edited the code in your question so that the four methods all print different messages. Now all you have to do to figure this out for yourself is to instantiate B and watch stdout. Please review and roll back if that's not what you actually meant. – Caleb Sep 24 '11 at 13:37

6 Answers6

81
  • Construction always starts with the base class. If there are multiple base classes then, construction starts with the left most base. (side note: If there is a virtual inheritance then it's given higher preference).
  • Then the member fields are constructed. They are initialized in the order they are declared
  • Finally, the class itself is constructed
  • The order of the destructor is exactly the reverse

Irrespective of the initializer list, the call order will be like this:

  1. Base class A's constructor
  2. class B's field named a (of type class A) will be constructed
  3. Derived class B's constructor
mpb
  • 1,277
  • 15
  • 18
iammilind
  • 68,093
  • 33
  • 169
  • 336
24

Assuming there is not virtual/multiple inheritance (that complicates things quite a bit) then the rules are simple:

  1. The object memory is allocated
  2. The constructor of base classes are executed, ending with most derived
  3. The member initialization is executed
  4. The object becomes a true instance of its class
  5. Constructor code is executed

One important thing to remember is that until step 4 the object is not yet an instance of its class, becuse it gains this title only after the execution of the constructor begins. This means that if there is an exception thrown during the constructor of a member the destructor of the object is not executed, but only already constructed parts (e.g. members or base classes) will be destroyed. This also means that if in the constructor of a member or of a base class you call any virtual member function of the object the implementation called will be the base one, not the derived one. Another important thing to remember is that member listed in the initialization list will be constructed in the order they are declared in the class, NOT in the order they appear in the initialization list (luckily enough most decent compilers will issue a warning if you list members in a different order from the class declaration).

Note also that even if during the execution of constructor code the this object already gained its final class (e.g. in respect to virtual dispatch) the destructor of the class is NOT going to be called unless the constructor completes its execution. Only when the constructor completes execution the object instance is a real first class citizen among instances... before that point is only a "wanna-be instance" (despite having the correct class).

Destruction happens in the exact reverse order: first the object destructor is executed, then it loses its class (i.e. from this point on the object is considered a base object) then all members are destroyed in reverse declaration order and finally the base class destruction process is executed up to the most abstract parent. As for the constructor if you call any virtual member function of the object (either directly or indirectly) in a base or member destructor the implementation executed will be the parent one because the object lost its class title when the class destructor completed.

6502
  • 112,025
  • 15
  • 165
  • 265
  • @Wolf: Yes, this is guaranteed. If you have class `D` that is derived from `B` then when instantiating `D` first the constructor of `B` is executed (the most abstract) and then the constructor `D` is executed. Actually the object becomes a real `D` (in respect to virtual methods) only after the construction of all bases and all other members has been completed (this is the tricky point about calling virtual methods of `D` during the construction of one of the members or during the construction of a base sub-object). – 6502 Jun 20 '14 at 06:04
  • It's not the construction order from bases to derived that I asked for, it's the word `abstract` which is misleading, I think. As I learned, an abstract class is a class with at least on pure virtual method. Please have a look at [this example](http://coliru.stacked-crooked.com/a/2f5e712f8421d303) – Wolf Jun 20 '14 at 07:27
  • @Wolf: Of course it's hard to discuss when the terms are interpreted differently ("When I use a word," Humpty Dumpty said, in rather a scornful tone, "it means just what I choose it to mean—neither more nor less.") :-) . However it's very popular the parlance that if you have `A <= B <= C <= D` (with `X <= Y` meaning "`X` is a base of `Y`") then `A` is the "most abstract" and `D` the "most concrete" class, independently from pure virtual methods. I'll reword anyway using "most derived". – 6502 Jun 20 '14 at 07:48
7

Base classes are always constructed before data members. Data members are constructed in the order that they are declared in the class. This order has nothing to do with the initialization list. When a data member is being initialized, it will look through your initialization list for the parameters, and call the default constructor if there is no match. Destructors for data members are always called in the reverse order.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
3

Base class constructor always executes first.so when you write a statement B b; the constructor of A is called first and then the B class constructor.therefore the output from the constructors will be in a sequence as follows:

A() C-tor
A() C-tor
B() C-tor
shubhendu mahajan
  • 816
  • 1
  • 7
  • 16
1
#include<iostream>

class A
{
  public:
    A(int n=2): m_i(n)
    {
    //   std::cout<<"Base Constructed with m_i "<<m_i<<std::endl;
    }
    ~A()
    {
    // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; 
     std::cout<<m_i;
    }

  protected:
   int m_i;
};

class B: public A
{
  public:
   B(int n ): m_a1(m_i  + 1), m_a2(n)
   {
     //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl;
   }

   ~B()
   {
   //  std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; 
     std::cout<<m_i;//2
     --m_i;
   }

  private:
   A m_a1;//3
   A m_a2;//5
};

int main()
{
  { B b(5);}
  std::cout <<std::endl;
  return 0;
}

The answer in this case is 2531. How constructor are called here:

  1. B::A(int n=2) constructor is called
  2. B::B(5) constructor is called
  3. B.m_A1::A(3) is called
  4. B.m_A2::A(5) is called

The same-way Destructor is called:

  1. B::~B() is called. i.e m_i = 2, which decrement m_i to 1 in A.
  2. B.m_A2::~A() is called. m_i = 5
  3. B.m_A1::~A() is called. m_i = 3 4 B::~A() is called., m_i = 1

In this example, construction of m_A1 & m_A2 is irrelevant of order of initialization list order but their declaration order.

NEERAJ SWARNKAR
  • 427
  • 4
  • 9
0

Output from the modified code is:

A() C-tor
A() C-tor
B() C-tor
~B() D-tor
~A() D-tor
~A() D-tor
Caleb
  • 124,013
  • 19
  • 183
  • 272