C++ Virtual Destructor and Inheritence

It is a well-known idiom to define a virtual destructor for the classes with virtual functions. If we don't define a virtual destructor, then the base class destructor will be invoked when you are deleting the object through the base class pointer even if the object is an instance of derived class.

Although this sounds intuitive, it is non-trivial to the C++ compiler implementation. Some extra care should be paid if the programmer didn't specify the desturctor. For example, in the following code, the base class A has a virtual destructor and the derived class B does not define any destructor. What will happen when after running delete a?

class A {
public:
  virtual ~A() { }
};

class B : public A {
};

int main() {
  A *a = new B();
  delete a;
}

If your compiler is following the Itanium C++ ABI, then the compiler will synthesize two functions _ZN1BD0Ev() and _ZN1BD2Ev().

  • The _ZN1BD2Ev() is the synthesized destructor which will destruct the instance members of class B and will in turn call the _ZN1AD2Ev() to destruct the members of class A.
  • The _ZN1BD0Ev() will invoke _ZN1BD2Ev() to destruct the object and deallocate the memory with operator delete (void *) (or namely _ZdlPv().)

Back to the example, when the delete a is executed, the underlying code sequence will get the second function pointer (index 1) from the virtual method table and pass the object address to the corresponding function, which is _ZN1BD0Ev() in this case.

In fact, if you define the destructor in the derived class, then your destructor will be part of the _ZN1BD2Ev(). You can check the behavior with the following code:

extern "C" void test() throw ();

class A {
public:
  virtual ~A() { }
};

class B : public A {
public:
  virtual ~B() {
    test();
  }
};

int main() {
  A *a = new B();
  delete a;
}

p.s. You can generate the LLVM assembly with clang++ -S -emit-llvm which is more readable.