The Need for Virtual Destructors in C++

by Cliff Green, Copyright © 1995, 1998, 2000

This page last
updated on:

May 16, 2001

Contents and Related Links

CodeWrangler Pages

Overview

As a general rule, a destructor should be declared virtual in a base class. There is a potential problem when destroying derived objects which are treated as base objects. The problem is that the destructor for the derived object is not invoked when the derived object is destroyed through a base pointer.

Assume a class D is derived from a base class B. There is a destructor defined for D, and the B destructor is not declared virtual. First a simple example to point out the pitfall:

  void f (B* b_ptr) {
    // ... call some B functions
    delete b_ptr;
  }

  int main () {
    // ...
    if (some_condition) {
      B* b_ptr = new B;
      // ... do something with the B object
      f (b_ptr);
    }
    else {
      D* d_ptr = new D;
      // ... do something with the D object
      f (d_ptr); // perfectly acceptable to pass a derived pointer as a base pointer
     // Ack!! may have problems from this point on
    }
  }
The second call of f will produce undesired behavior, as D's destructor is not called. This can be a subtle, hard-to-find, and potentially disastrous bug.

On a side note, a recommended design guideline is to always new and delete objects at the same functional level. The logic used in this simple example creates and destroys objects at two different levels of the call hierarchy rather than the same one. Keeping object allocation and deallocation at the same conceptual level makes for a simpler design and for easier debugging and maintenance.

A more realistic example without the bad design style:

  const B* find_oldest(const B* b_array[]) {  // find oldest object in B ptr array
    // ... selection code
    return b_array[selected_index];
  }

  int main () {
    // ...
    B* b_array[MAX_SIZE];
    // ... create the list of B and D object pointers (and possibly containing other
    // derived object pointers) by newing B or D (or other) objects

    B* selected_ptr = find_oldest (b_array);
    delete selected_ptr;
  }
If the selected (oldest) object which is deleted is a derived object, it's destructor is not called, which can make for a very hard to detect bug.

To avoid this pitfall, some developers declare destructors virtual in all classes. My opinion is that blindly following this rule will add overhead in classes that can't afford it. Remember that a virtual function in a class will add an extra word of storage to any objects created of that type. In addition, when a virtual function is called, an extra level of indirection is needed for the invocation of that function. For many classes, this space and performance penalty can be prohibitive. For example, a complex number or pixel encapsulation class would not want virtual destructors, since they are typically or frequently allocated in large arrays or containers.

Many base classes, however, already have one or more virtual functions declared. In this case, declaring another virtual function does not add any additional overhead. Also, base classes with virtual functions are more likely to be used in situations where the problem can occur.

My recommendation is to declare destructors virtual in base classes, as long as the overhead is not going to be an issue. If the class is stand-alone and will not be used as a base class, then I don't declare the destructor as being virtual. If there's already at least one virtual function in the base class, then the destructor is always declared virtual.


This page constructed by Cliff Green, Copyright © 2001.