When a class design is very complex, then the order of call of constructors and destructors becomes very important. For instance, assume that there is a design where there are base classes, derived classes, multiple inheritance and member objects in these classes. Now, the calling constructors and destructors shall follow a specific order as shown below:
Constructor order
- Base class member objects (in order of occurrence inside base class)
- Base class constructors (in order of specification for multiple inheritance)
- Derived class member objects (in order of occurrence inside derived class)
- Derived class constructor
Destructor order
- This will be follow exactly opposite order followed by the constructor
#include <iostream>
using namespace std;
class Base1member
{
public:
Base1member() { cout << "Base1member()" << endl; }
~Base1member(){ cout << "~Base1member()" << endl; }
};
class Base2member
{
public:
Base2member() { cout << "Base2member()" << endl; }
~Base2member(){ cout << "~Base2member()" << endl; }
};
class Derivedmember
{
public:
Derivedmember() { cout << "Derivedmember()" << endl; }
~Derivedmember(){ cout << "~Derivedmember()" << endl; }
};
class Base1
{
Base1member b1;
public:
Base1() { cout << "Base1()" << endl; }
~Base1(){ cout << "~Base1()" << endl; }
};
class Base2
{
Base2member b2;
public:
Base2() { cout << "Base2()" << endl; }
~Base2(){ cout << "~Base2()" << endl; }
};
class Derived : public Base2, public Base1
{
Derivedmember d;
public:
Derived() { cout << "Derived()" << endl; }
~Derived(){ cout << "~Derived()" << endl; }
};
int main()
{
Derived dobj;
}
The output is:
Base2member()
Base2()
Base1member()
Base1()
Derivedmember()
Derived()
~Derived()
~Derivedmember()
~Base1()
~Base1member()
~Base2()
~Base2member()
No relation to constructor initializer list :
It’s also interesting to note that the order of constructor calls for member objects is completely unrelated to the order of the calls in the constructor initializer list. The order is determined by only one order which is how the member objects are declared in the class.
This is because, if the C++ compiler allows to change order of constructor calls due to its constructor initializer list then it will have side-effects. For example, a class may end up having two different constructor call sequences when such a class contains two different constructors.
However, then the destructor cannot know that which destructor order will be considered for properly destructing the objects. This will end up in a dependency problem.
Example of 2 constructors having different initializer list order:
there are 2 constructors in class MainFunda and both are specifying different order of initialization of member objects in constructor initializer lists. However, we will see that compiler will completely ignore such lists and follow the logical order explained above:
#include <iostream> //main header
using namespace std;//for namespace
class member1
{
public:
member1() { cout << "member1()" << endl; }
~member1(){ cout << "~member1()" << endl; }
};
class member2
{
public:
member2() { cout << "member2()" << endl; }
~member2(){ cout << "~member2()" << endl; }
};
class MainFunda
{
member1 m1;
member2 m2;
public:
MainFunda() : m2(), m1() //Initialization Order#1
{
cout << "MainFunda()" << endl;
}
MainFunda(int x) : m1(), m2() //Initialization Order#2
{
cout << "MainFunda(int)" << endl;
}
~MainFunda()
{
cout << "~MainFunda()" << endl;
}
};
int main()
{
MainFunda a1;
MainFunda a2(8);
}
The output shows same order of construction of member1 & member2 in both:
member1()
member2()
MainFunda()
member1()
member2()
MainFunda(int)
~MainFunda()
~member2()
~member1()
~MainFunda()
~member2()
~member1()