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.
- Construction always starts with the base
class
. If there are multiple base class
es 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:
- Base
class A
's constructor
class B
's field named a
(of type class A
) will be constructed
- Derived
class B
's constructor
Assuming there is not virtual/multiple inheritance (that complicates things quite a bit) then the rules are simple:
- The object memory is allocated
- The constructor of base classes are executed, ending with most derived
- The member initialization is executed
- The object becomes a true instance of its class
- 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.
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.
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
#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:
- B::A(int n=2) constructor is called
- B::B(5) constructor is called
- B.m_A1::A(3) is called
- B.m_A2::A(5) is called
The same-way Destructor is called:
- B::~B() is called. i.e m_i = 2, which decrement m_i to 1 in A.
- B.m_A2::~A() is called. m_i = 5
- 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.
Output from the modified code is:
A() C-tor
A() C-tor
B() C-tor
~B() D-tor
~A() D-tor
~A() D-tor