When should your destructor be virtual?

Date:May 7, 2004 / year-entry #178
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20040507-00/?p=39443
Comments:    20
Summary: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...

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. Mike G. 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. Daniel 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. Raymond Chen says:

    Fixed, thanks Daniel.

  4. Raymond Chen 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. Raymond Chen says:

    I can’t think of one offhand.

  6. Aarrgghh says:

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

  7. DrPizza 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. When should your destructor be virtual?…

  9. Thomas Maeder 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. Raymond Chen 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. Marc Wallace 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. jeffdav says:

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

  13. Troll Buster 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. Raymond Chen 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. Florin POPA 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. The Other John 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. vinod says:

    Graphics means use of graphics function

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

  18. Atul Malhotra 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. Raymond Chen says:

    Commenting on this article has been closed.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index