Non-virtual destructors


I got a user submitted question to CppQuiz.org the other day where the contributor had gotten the answer wrong, and I didn’t notice at first. The question was about non-virtual destructors:

#include <iostream>

struct B {
  B() {
    std::cout << 'b';
  }
  ~B() {
    std::cout << 'B';
  }
};

struct D : B {
  D() {
    std::cout << 'd';
  }
  ~D() {
    std::cout << 'D';
  }
};

int main() {
  B* p = new D;
  delete p;
}

As usual on CppQuiz, the objective is to figure out the output of the program, according to the C++ standard. Before continuing, please try to find the answer yourself!

What does the standard say?

Did you reply bdB? So did the person who contributed the question, and I initially went “yep, that seems logical”. It does indeed seem logical that these functions are called, but what exactly happens when we fail to destroy the derived part of an object? Let’s have a look at §5.3.5/3 in the C++11 standard:

if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.

The static type here is B, which is indeed a base class of the dynamic type D. The static type B does however not have a virtual destructor, so the behavior is undefined. (I have yet to see a compiler who doesn’t print bdB though.)

So why is this undefined behavior? It would be hard for the standard to specify exactly what happens when an object is not properly destroyed.

In practice, most compilers will probably go with the normal rules for virtual functions, print bdB, and simply not destroy the D part of the object. But since the behavior is undefined, there’s no guarantee for this.

One could argue that even if the result of not destroying D properly is undefined, the standard could still mandate which destructor(s) actually get called. However, when undefined behavior occurs in a program, the entire execution is undefined, so there really would be no point.

If you’re curious about undefined behavior, I’ve written about it before. That article also has links to some really interesting, more in-depth articles about the subject.

As usual, the code for this blog post is available on GitHub.

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s

%d bloggers like this: