C++ language has the feature called inheritance. Base class can be derived to another class and further in the inheritance level. Multiple levels of inheritance is possible. An object of type derived class has to be created and destructed in proper way. Let us understand the creation and destruction of any c++ object.

Constructor call sequence

C++ compiler ensures the constructor will be called in the order how they are getting created. Base class will be created and then derived class. So the sequence in which constructors are called is in the same order in the inheritance hierarchy. Base construction will be called and then the derived ones. Compiler maintains compile time static table for constructors and they often known as ctor table.

constructor call sequence

Destructor call sequence

Now comes the destruction part. Destruction cannot happen in the order like construction happened. Base has to be there till the end before derived is destructed. So destruction happens in the opposite sequence than construction. First derived class will be destroyed and then base class. Compiler maintains compile time static table for destructor and they often known as dtor table. Sequence of destructor events are called can be illustrated in the below flow diagram.

destructor call sequence

Now let us understand with a program. Here we want to see how the sequence will be.

class base 
{
  public: base()
  {
    cout << "base construction\n";
  }
  ~base()
  {
    cout << "base destruction\n";
  }
};
class derived : public base
{
  char *name;
  public : derived ()
  {
    cout << "derived construction\n";
  name = new char[10];
  cout << "mem allocated for name\n";
  
  }
  ~ derived ()
  {
    cout << "derived destruction\n";
  delete [] name;
  cout << "mem de-allocated for name\n";
  }
};
int main (int argc, char *argv[])
{
  derived *= new derived();
  delete d;
}

output:

base construction
derived construction
mem allocated for name
derived destruction
mem de-allocated for name
base destruction

Destructor call sequence with base pointer

Now let us see what would happens when the derived object is deleted with base type pointer

int main (int argc, char *argv[])
{
  base *= new derived();
  delete b;
}

output:

base construction
derived construction
mem allocated for name
base destruction

Here derived destruction event is missing and derived destructor is not getting called by compiler. This is common problem when classes are designed with early binding or compile time binding. As the type of the object is base and as per the static dtor table compiler only calls base destructor. This also leads to memory leak as string member name is not getting de-allocated.

Virtual destructor

To ensure proper destructor call sequence, we must make the base destructor as virtual. This ensures dynamic/runtime binding of the destructor through vtable mechanism. Compiler do not use dtor table when destructor is virtual. Like normal virtual function it will loop up the address vptr -> vtable entry and call indirectly.

class base 
{
  public: base()
  {
    cout << "base construction\n";
  }
  virtual ~base() 
  {
    cout << "base destruction\n";
  }
};

C++ compiler will create a vtable and put the first entry as destructor function. vptr of base will point to vtable of base class. So in the early stage base will be created and the vtable entry 0 will point to the destructor of base ~base().

base-constructor-vptr-vtable

Now derived will be created and compiler will overwrite the vptr with the address of the vtable of derived. derived vtable entry 0 will point this to function ~derived().

derived-constructor-vptr-vtable

Vtable assignment in construction chain

constructor-call-sequence-base-derived

Destructor chain and call sequence

Now a delete on base pointer is called. Since this base destructor is virtual one, compiler will never call ~base() by dtor table. Instead this will lookup vptr and then vtable function 0. Then this will be called using function pointer. As per last constructed override, vptr points to vtable of derived class and vtable function 0 location points to derived destructor i.e. ~derived(). Eventually derived destructor will be called. Now after exit of ~derived() compiler will assign vptr to vtable of base class.

At this point object is having vptr pointing to vtble of base. Compiler will again lookup address of destructor and call ~base(). After the exit of ~base() vptr will be assigned to NULL. No more virtual call is possible and object is at highest level in the inheritance hierarchy so the delete call will ended.

destructor-call-sequence-derived-base

Here we have illustrated two levels of inheritance. However this inheritance level can be any level and this mechanism will work fine. After modifying the prototype ~base() to virtual ~base() the sequence will be as expected as below.

class base 
{
  public: base()
  {
    cout << "base construction\n";
  }
  virtual ~base() 
  {
    cout << "base destruction\n";
  }
};

Proper Output:
Output after this change:
base construction
derived construction
mem allocated for name
derived destruction
mem de-allocated for name
base destruction

About our authors: Team EQA

You have viewed 1 page out of 62. Your C++ learning is 0.00% complete. Login to check your learning progress.

#