Declare destructors virtual in a polymorphic base classes

let’s start with a simple example from Effective C++ Third Edition.

TimeKeeper Example
  1. class TimeKeeper
  2. {
  3. public:
  4.     TimeKeeper();
  5.     ~TimeKeeper();
  6. };
  7. class AtomicClock: public TimeKeeper{};
  8. class WaterClock : public TimeKeeper{};
  9. class WritstWatch: public TimeKeeper{};

for managing the creation of objects will create a factory function that returns a dynamically allocated object of a class derived from “TimeKeeper”.

  1. TimeKeeper* getTimeKeeper();

The objects returned by this functions are on the heap memory, so to avoid leaking the memory and other resources it’s important to clean up these objects constantly after using them.

  1. TimeKeeper *ptk = getTimeKeeper();
  2. delete ptk;

Suppose that (getTimeKeeper) returns a pointer to a derived class object (ex. AtomicClock), this object is being deleted via a base class pointer and the base class (TimeKeeper) has a non-virtual destructor, this may lead to a disaster as C++ specifies that when a derived class object is deleted through a pointer to a base class with a non-virtual destructor, results are undefined.
What happens in runtime is that the derived part of the object is never destroyed, the data members of the derived class object probably not to be destroyed nor the destructor run, although the base class part will be destroyed and this will lead to a partially destroyed object, and this is a way of leaking resources.
Eliminating this problem is very simple, by giving the base class a virtual destructor  like this.

  1. class TimeKeeper
  2. {
  3. public:
  4.     TimeKeeper();
  5.     virtual ~TimeKeeper();
  6. };
  7. class AtomicClock: public TimeKeeper{};
  8. class WaterClock : public TimeKeeper{};
  9. class WritstWatch: public TimeKeeper{};
  10. TimeKeeper *ptk = getTimeKeeper();
  11. delete ptk;  // now behaves correctly

If a class does not contain virtual functions, that often indicates it won’t be used as a base class. When a class is not intended to be a polymorphic class, making a virtual destructor is usually a bad idea.

Point Class Example
  1. class Point
  2. {
  3. public:
  4.     Point(int xCoord, int yCoord);
  5.     ~Point();
  6. private:
  7.     int x, int y;
  8. };

if an int occupies 32 bits, a Point class object can fit into 64-bit register, the implementation of virtual functions requires the objects carry information that can be used at runtime to determine which virtual functions should be invoked on the object.

This information typically takes the form of a pointer called vptr  (Virtual Table Pointer) , this pointer points to an array of function pointers called vtbl (Virtual Table) each class with virtual functions has an associated vtbl, when a virtual functions is invoked on an object, the actual function called is determined by following the object’s vptr to a vtbl and then looking up the appropriate function pointer in vtbl.

Regarding the class “Point” if the it contains a virtual function, objects of type “Point” will increase in size, on 32-bit architecture, they’ll go from 64 bits (for the two ints x,y) to 96 bits (for the ints + vptr) and from 64 to 128 bits in 64-bit architecture, because pointer on these architectures are 64-bit size, by adding  vptr to “Point” the size will be increased by 50-100% ! No longer can “Point” objects fit in a 64-bit register.

We can summarize this topic in two points

Polymorphic base classes should declare virtual desturcotrs, If a class has any virtual functions, it should have a virtual destructor.

Classes not designed be used polymorphically should not declare virtual destructors.

But take care NOT all base classes are designed to be used polymorphically, for example standard string.

  1. class SpecialString : public std::string{ …. };

This is a bad idea as std::string has non-virtual destructor and not designed to be used polymorphically and if anyone tried to convert pointer to SpeicalString to pointer to std::string and then used delete this will lead to undefined behavior and SpecialString resources will be leaked as the destructor won’t be called, and you’ll find this -lacking of virtual destructor- in all STL types also (e.g vector, list, set, … etc).Unfortunately,  C++ doesn’t offers derivation-prevention mechanism in contrast to Java’s final classes or C#’s sealed classes.

So, DON’T forget Not all base classes are designed to be used polymorphically.

References:
  • Effective C++, Third Edition —Scott Meyers.
  • Professional C++— Nicholas A. Solter Scott J. Kleper. 
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s