Constructors and Destructors in C++

By Andrei Milea

Constructor and Destructor Order

The process of creating and deleting objects in C++ is not a trivial task. Every time an instance of a class is created the constructor method is called. The constructor has the same name as the class and it doesn't return any type, while the destructor's name it's defined in the same way, but with a '~' in front:

class String
{

public:

String() //constructor with no arguments
    :str(NULL),
    size(0)
{

}

String(int size) //constructor with one argument
    :str(NULL),
    size(size)

{
    str = new char[size];
}


~String() //destructor
{
    delete [] str;
};

private:

    char *str;

    int size;

}
Even if a class is not equipped with a constructor, the compiler will generate code for one, called the implicit default constructor. This will typically call the default constructors for all class members, if the class is using virtual methods it is used to initialize the pointer to the virtual table, and, in class hierarchies, it calls the constructors of the base classes. Both constructors in the above example use initialization lists in order to initialize the members of the class.

The construction order of the members is the order in which they are defined, and for this reason the same order should be preserved in the initialization list to avoid confusion.

To master developing and debugging C++ applications, more insight is required regarding the way constructors and destructors work. Some problems arise when we are dealing with class hierarchies. Let's take a look at the following example where class B is inherited from class A:
class A
{

public:

A(int m,int n)
    :m(m),
    n(n)
{
    cout<<"constructor A"<<m<<n;
};

~A()
{

};

private:

int m;
int n;

};


class B : public A

{

public:

B()
    :b(5) //error : default constructor to initialize A is not found
{
    cout<<"constructor B"<<b;
}

~B()
{};

private:

int b;

};


int main()
{

B x;
return 0;

};
When we create an object of type B, the A part of the B object must be initialized and since we provide a constructor for class A, the compiler will not create an implicit default constructor. This code will fail to compile because there is no default constructor for class A to be called. To fix this we could provide a default constructor in class A or explicitly call the existing constructor of A in the initialization list of the B's constructor:
B()
:A(3,4),
b(5)
{
    cout<<"constructor B"<<b;
}
Notice that we needed to call the constructor of A before doing any initialization in B, since the order of construction starts with the base class and ends with the most derived class.

Never Use Virtual Methods in Constructors or Destructors

A side effect of this behavior is that you should avoid calling virtual functions in a class's constructor (or destructor). The problem is that if a base class makes a virtual function call implemented by the derived class, that function may be implemented in terms of members that have not yet been initialized (think of the pain that would cause!). C++ solves this problem by treating the object as if it were the type of the base class, during the base class constructor, but this defeats the whole purpose of calling a virtual function in a constructor. Destruction, by the way, works in the same way. If you're interested, Scott Meyers has a very nice writeup of this principle taken from his book Effective C++: 55 Specific Ways to Improve Your Programs and Designs .

The destruction order in derived objects goes in exactly the reverse order of construction: first the destructors of the most derived classes are called and then the destructor of the base classes.

Virtual Destructors

To build an object the constructor must be of the same type as the object and because of this a constructor cannot be a virtual function. But the same thing does not apply to destructors. A destructor can be defined as virtual or even pure virtual. You would use a virtual destructor if you ever expect a derived class to be destroyed through a pointer to the base class. This will ensure that the destructor of the most derived classes will get called:
A* b1 = new B;
delete b1;
If A does not have a virtual destructor, deleting b1 through a pointer of type A will merely invoke A's destructor. To enforce the calling of B's destructor in this case we must have specified A's destructor as virtual:
virtual ~A();
It is a good idea to use a virtual destructor in any class that will be used as an interface (i.e., that has any other virtual method). If your class has no virtual methods, you may not want to declare the destructor virtual because doing so will require the addition of a vtable pointer.

The destructor is called every time an object goes out of scope or when explicitly deleted by the programmer(using operator delete). The order in which local objects are implicitly deleted when the scope they are defined ends is the reverse order of their creation:
void f()
{
    string a(.some text.);
    string b = a;
    string c;
    c = b;
}
At the end of function f the destructors of objects c, b, a (in this order) are called and the same memory is deallocated three times causing undefined and wrong behavior. Deleting an object more than one time is a serious error. To avoid this issue, class string must be provided with a copy constructor and a copy assignment operator which will allocate storage and copy the members by values. The same order of construction/destruction applies for temporary objects that are used in expressions or passed as parameters by value. However, the programmer is usually not concerned with temporary objects since they are managed by the C++ compiler.

Unlike local objects, static objects are constructed only the first time their definition is reached and destroyed at the end of the program.

By the way, if you liked the details about how C++ works, you might enjoy Scott Meyer's excellent book on C++: