Constructors and Destructors in C++ – Part 2

In our previous post we discussed constructors and destructors for local objects. Now, suppose we have the same hierarchy of classes, Base, Derived and DerivedAgain, and we constructed a DerivedAgain object the heap, like so:

int main()
{
    DerivedAgain* derivedAgainObject = new DerivedAgainObject();
    return 0;
}

When we run this we will see the following,

Base Constructor
Derived Constructor
DerivedAgain Constructor

The constructors were called in the same order as before. However, because our DerivedAgain object was created on the heap the destructor was never called. If we want to destroy our object we must explicitly use delete.

int main()
{
    DerivedAgain* derivedAgainObject = new DerivedAgainObject();
    delete derivedAgainObject;
    return 0;
}

The above will output the following to the command line,

Base Constructor
Derived Constructor 
DerivedAgain Constructor
DerivedAgain Destructor
Derived Destructor
Base Destructor

Let’s consider what happens when we have a pointer of type Derived that points to an object of typed DerivedAgain. Say we run:

Derived* derivedPointer = new DerivedAgain();
delete derivedPointer;
return 0;

When we run this code we will see:

Base Constructor
Derived Constructor
DerivedAgain Constructor
Derived Destructor
Base Destructor

The constructors are the same, because we are creating a DerivedAgain object as before. However, now we are calling delete on a pointer of type Derived. So only the Derived and Base class destructors are called.

We can avoid this problem by making our destructors virtual.

class Base
{
public:
        Base()
        {
                std::cout << "Base Constructor" << std::endl;
        }

        virtual ~Base()
        {
                std::cout << "Base Destructor" << std::endl;
        }
};

class Derived : public Base
{
public:
        Derived() : Base()
        {
                std::cout << "Derived Constructor" << std::endl;
        }

        virtual ~Derived()
        {
                std::cout << "Derived Destructor" << std::endl;
        }
};

class DerivedAgain : public Derived
{
public:
        DerivedAgain() : Derived()
        {
                std::cout << "DerivedAgain Constructor" << std::endl;
        }

        ~DerivedAgain()
        {
                std::cout << "DerivedAgain Destructor" << std::endl;
        }
};

Now when we run,

int main()
{
        Derived* derivedPointer = new DerivedAgain();
        delete derivedPointer;
        return 0;
}

we will see:

Base Constructor
Derived Constructor
DerivedAgain Constructor
DerivedAgain Destructor
Derived Destructor
Base Destructor

Even though we have a pointer of type Derived, because the destructor is virtual, when we call delete, the DerivedAgain destructor gets called. What’s interesting here, is that we always call all the destructors upwards through the type hierarchy, the use of virtual ensures that we start at the right level. For this reason, we should usually make our destructors virtual.

We can even make our destructors pure virtual. However, we will have to give this pure virtual destructor a body. It is a common misconception that a pure virtual function necessarily can’t have a body, this is not true. However pure virtual destructors are probably one of the only examples of when you would want to do it. The only reason we would want to make a destructor pure virtual is that we want to make a class abstract and the destructor is the only method. In my opinion this is a very unlikely to happen, so it’s not something we should really worry about.

In the next post we will have a look at what happens when things go wrong in construction and destruction.

Leave a Reply

Your email address will not be published. Required fields are marked *