When should your destructor be virtual?


When should your C++ object's destructor be virtual?

First of all, what does it mean to have a virtual destructor?

Well, what does it mean to have a virtual method?

If a method is virtual, then calling the method on an object always invokes the method as implemented by the most heavily derived class. If the method is not virtual, then the implementation corresponding to the compile-time type of the object pointer.

For example, consider this:

class Sample {
public:
 void f();
 virtual void vf();
};

class Derived : public Sample {
public:
 void f();
 void vf();
}

void function()
{
 Derived d;
 Sample* p = &d;
 p->f();
 p->vf();
}

The call to p->f() will result in a call to Sample::f because p is a pointer to a Sample. The actual object is of type Derived, but the pointer is merely a pointer to a Sample. The pointer type is used because f is not virtual.

On the other hand, the call to The call to p->vf() will result in a call to Derived::vf, the most heavily derived type, because vf is virtual.

Okay, you knew that.

Virtual destructors work exactly the same way. It's just that you rarely invoke the destructor explicitly. Rather, it's invoked when an automatic object goes out of scope or when you delete the object.

void function()
{
 Sample* p = new Derived;
 delete p;
}

Since Sample does not have a virtual destructor, the delete p invokes the destructor of the class of the pointer (Sample::~Sample()), rather than the destructor of the most derived type (Derived::~Derived()). And as you can see, this is the wrong thing to do in the above scenario.

Armed with this information, you can now answer the question.

A class must have a virtual destructor if it meets both of the following criteria:

  • You do a delete p.
  • It is possible that p actually points to a derived class.

Some people say that you need a virtual destructor if and only if you have a virtual method. This is wrong in both directions.

Example of a case where a class has no virtual methods but still needs a virtual destructor:

class Sample { };
class Derived : public Sample
{
 CComPtr<IStream> m_p;
public:
 Derived() { CreateStreamOnHGlobal(NULL, TRUE, &m_p); }
};

Sample *p = new Derived;
delete p;

The delete p will invoke Sample::~Sample instead of Derived::~Derived, resulting in a leak of the stream m_p.

And here's an example of a case where a class has virtual methods but does not require a virtual destructor.

class Sample { public: virtual void vf(); }
class Derived : public Sample { public: virtual void vf(); }

Derived *p = new Derived;
delete p;

Since the object deletion occurs from the pointer type that matches the type of the actual object, the correct destructor will be invoked. This pattern happens often in COM objects, which expose several virtual methods corresponding to its interfaces, but where the object itself is destroyed by its own implementation and not from a base calss pointer. (Notice that no COM interfaces contain virtual destructors.)

The problem with knowing when to make your destructor virtual or not is that you have to know how people will be using your class. If C++ had a "sealed" keyword, then the rule would be simpler: If you do a "delete p" where p is a pointer to an unsealed class, then that class needs have a virtual destructor. (The imaginary "sealed" keyword makes it explicit when a class can act as the base class for another class.)

Comments (20)
  1. Anonymous says:

    "Some people say that you need a virtual destructor if and only if you have a virtual method. This is wrong in both directions."

    Well, almost.

    If the sizeof or layout of your class is a concern for some reason, making the destructor virtual "costs nothing" if your base class already has a virtual method. You’re already carrying a vptr at that point.

    There’s rarely a reason NOT to make the destructor virtual if you already have a virtual method, I think. (I guess it’s possible you’re going to create a LOT of temporary Derived’s, so the ~Derived virtual call becomes a bottleneck, but that seems very unlikely…)

    An empty virtual destructor for your last sample class would hardly be a maintenance headache, and opens the possibility for the use case to change down the road without base class changes.

  2. Anonymous says:

    If you really don’t want a virtual destructor, you should probably make the base class’ destructor protected, to prevent mistakes.

    Also, there’s a small mistake – Derived isn’t derived in the last bit of code.

  3. Anonymous says:

    Fixed, thanks Daniel.

  4. Anonymous says:

    Mike G.: Most COM objects do not have a virtual destructor since they are destructed from the derived class (inside the Release() methods). Yes, it doesn’t cost you much (an etra pointer in the vtable), but if your object architecture already encapsulates object destruction in a virtual method (like COM does), then virtualizing the destructor may end up creating confusion.

  5. Anonymous says:

    I can’t think of one offhand.

  6. Anonymous says:

    Marc Wallace: You might want to do that if you’re testing the debug heap’s memory-leak detection.

  7. Anonymous says:

    "There’s rarely a reason NOT to make the destructor virtual if you already have a virtual method, I think. (I guess it’s possible you’re going to create a LOT of temporary Derived’s, so the ~Derived virtual call becomes a bottleneck, but that seems very unlikely…) "

    One reason is that it needn’t be necessary.

    Consider for example:

    struct Base

    {

    arbitraryType arbitraryMemberVariable;

    virtual void doSomething() = 0;

    ~Base() {}

    };

    struct Derived : Base

    {

    arbitraryType arbitraryMemberVariable;

    virtual void doSomething() { blahblahblah; }

    ~Derived() {}

    };

    // any old block for any old reason

    {

    const Base& b((Derived()));

    }

    In this case the Derived d-tor will be called in spite of being bound to a reference to Base, because the compiler uses the static type of the temporary to figure out which destructor to call.

    Esoteric? Sure. Useless? Hell no, as anyone who’s seen ScopeGuard will know.

  8. Anonymous says:

    When should your destructor be virtual?…

  9. Anonymous says:

    Slight inaccuracy:

    The statement

    The delete p will invoke Sample::~Sample instead of Derived::~Derived, resulting in a leak of the stream m_p.

    is wrong. If a delete expression is evaluated on a base class pointer that points to an instance of a derived class, the behavior is undefined; anything can happen.

    It is possible that the base class destructor is invoked and the derived clas destructor isn’t, but that’s just an instance of undefined behavior. Other instances are a program crash or the computer catching fire.

  10. Anonymous says:

    Ah, excellent point Thomas.

    5.3.5.3: "If the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined."

  11. Anonymous says:

    Are there any cases where you would *not* want to invoke the destructor of the most derived class that has it?

    In other words: somewhere, something knows that p isa Derived. Are there cases when Derived::~Derived() is defined, but you would actually want "delete p" to do a Simple::~Simple()?

  12. Anonymous says:

    There you go giving away my interview questions again, Raymond.

  13. Anonymous says:

    Don’t click on the link in Gary Niger’s post. It’s a GNAA troll link. Like goatse.cx only a thousand times worse.

  14. Anonymous says:

    Thanks, Mr. Troll Buster. I deleted the comment. It was also a clipboard stealer. Whatever text was on your clipboard got uploaded to the site, and then you got redirected to something unpleasant…

  15. Anonymous says:

    Hi,

    I think the rule about "When should your destructor be virtual? " can be:

    The DTOR of base class is:

    – public and MUST be virtual

    – protected and then should be non-virtual

    And by the way, is not a good ideea to inherit public from a class without any virtual method.

  16. Anonymous says:

    Wow, a clipboard stealer… never heard of that one before. Amazing the things that are possible.

    I wonder if we could perform a DNS attack on him by copying gigantic bitmaps into our clipboard, then visiting his site :)

  17. Anonymous says:

    Graphics means use of graphics function

    like circle(),line() etc., like Turbo c++.

  18. Anonymous says:

    Yupp, it means drawing objects using graphics functions using standard graphics library as well as using user defined classes which may or may not be inherited from the standard classes.

  19. Anonymous says:

    Commenting on this article has been closed.

Comments are closed.