When should you not use virtual destructors?

31,513

Solution 1

There is no need to use a virtual destructor when any of the below is true:

  • No intention to derive classes from it
  • No instantiation on the heap
  • No intention to store with access via a pointer to a superclass

No specific reason to avoid it unless you are really so pressed for memory.

Solution 2

To answer the question explicitly, i.e. when should you not declare a virtual destructor.

C++ '98/'03

Adding a virtual destructor might change your class from being POD (plain old data)* or aggregate to non-POD. This can stop your project from compiling if your class type is aggregate initialized somewhere.

struct A {
  // virtual ~A ();
  int i;
  int j;
};
void foo () { 
  A a = { 0, 1 };  // Will fail if virtual dtor declared
}

In an extreme case, such a change can also cause undefined behaviour where the class is being used in a way that requires a POD, e.g. passing it via an ellipsis parameter, or using it with memcpy.

void bar (...);
void foo (A & a) { 
  bar (a);  // Undefined behavior if virtual dtor declared
}

[* A POD type is a type that has specific guarantees about its memory layout. The standard really only says that if you were to copy from an object with POD type into an array of chars (or unsigned chars) and back again, then the result will be the same as the original object.]

Modern C++

In recent versions of C++, the concept of POD was split between the class layout and its construction, copying and destruction.

For the ellipsis case, it is no longer undefined behavior it is now conditionally-supported with implementation-defined semantics (N3937 - ~C++ '14 - 5.2.2/7):

...Passing a potentially-evaluated argument of class type (Clause 9) having a non-trivial copy constructor, a non-trivial move constructor, or a on-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.

Declaring a destructor other than =default will mean it's not trivial (12.4/5)

... A destructor is trivial if it is not user-provided ...

Other changes to Modern C++ reduce the impact of the aggregate initialization problem as a constructor can be added:

struct A {
  A(int i, int j);
  virtual ~A ();
  int i;

  int j;
};
void foo () { 
  A a = { 0, 1 };  // OK
}

Solution 3

I declare a virtual destructor if and only if I have virtual methods. Once I have virtual methods, I don't trust myself to avoid instantiating it on the heap or storing a pointer to the base class. Both of these are extremely common operations and will often leak resources silently if the destructor is not declared virtual.

Solution 4

A virtual destructor is needed whenever there is any chance that delete might be called on a pointer to an object of a subclass with the type of your class. This makes sure the correct destructor gets called at run time without the compiler having to know the class of an object on the heap at compile time. For example, assume B is a subclass of A:

A *x = new B;
delete x;     // ~B() called, even though x has type A*

If your code is not performance critical, it would be reasonable to add a virtual destructor to every base class you write, just for safety.

However, if you found yourself deleteing a lot of objects in a tight loop, the performance overhead of calling a virtual function (even one that's empty) might be noticeable. The compiler cannot usually inline these calls, and the processor might have a difficult time predicting where to go. It is unlikely this would have a significant impact on performance, but it's worth mentioning.

Solution 5

Not all C++ classes are suitable for use as a base class with dynamic polymorphism.

If you want your class to be suitable for dynamic polymorphism, then its destructor must be virtual. In addition, any methods which a subclass could conceivably want to override (which might mean all public methods, plus potentially some protected ones used internally) must be virtual.

If your class is not suitable for dynamic polymorphism, then the destructor should not be marked virtual, because to do so is misleading. It just encourages people to use your class incorrectly.

Here's an example of a class which would not be suitable for dynamic polymorphism, even if its destructor were virtual:

class MutexLock {
    mutex *mtx_;
public:
    explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
    ~MutexLock() { mtx_->unlock(); }
private:
    MutexLock(const MutexLock &rhs);
    MutexLock &operator=(const MutexLock &rhs);
};

The whole point of this class is to sit on the stack for RAII. If you're passing around pointers to objects of this class, let alone subclasses of it, then you're Doing It Wrong.

Share:
31,513

Related videos on Youtube

Mag Roader
Author by

Mag Roader

I am a professional video game developer working for Electronic Arts Tiburon. I am credited in a variety of franchises. I loves me some games.

Updated on December 12, 2021

Comments

  • Mag Roader
    Mag Roader over 2 years

    Is there ever a good reason to not declare a virtual destructor for a class? When should you specifically avoid writing one?

  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    fine overview. i dropped my answer in favour of this. +1 :)
  • Windows programmer
    Windows programmer over 15 years
    This is not a good answer. "There is no need" is different from "should not", and "no intention" is different from "made impossible".
  • CesarB
    CesarB over 15 years
    And, in fact, there is a warning option on gcc which warns on precisely that case (virtual methods but no virtual dtor).
  • Adam Rosenfield
    Adam Rosenfield over 15 years
    Also add: no intention to delete an instance via a base class pointer.
  • mxcl
    mxcl over 15 years
    This doesn't really answer the question. Where is your good reason not to use a virtual dtor?
  • Mag Roader
    Mag Roader over 15 years
    Don't you then run the risk of leaking memory if you derive from the class, regardless of whether you have other virtual functions?
  • sep
    sep over 15 years
    I think that when there is no need to do something, that is a good reason not to do it. Its following the Simple Design principle of XP.
  • sep
    sep over 15 years
    Polymorphism will certainly slow things down. Compare it with a situation where we need polymorphism and choose not to, it will be even slower. Example: we implement all the logic at the base class destructor, using RTTI and a switch statement to clean up resources.
  • Windows programmer
    Windows programmer over 15 years
    You're right, and I was wrong, performance isn't the only reason. But this shows I was right about the rest of it: the class's programmer had better include code to prevent the class from ever being inherited by anyone else.
  • Steve Jessop
    Steve Jessop over 15 years
    In C++, it's not your responsibility to stop me inheriting from your classes that you've documented are not suitable for use as base classes. It's my responsibility to use inheritance with caution. Unless house style guide says otherwise, of course.
  • Steve Jessop
    Steve Jessop over 15 years
    ... just making the destructor virtual doesn't mean the class will necessarily work correctly as a base class. So marking it virtual "just because", instead of making that assessment, is writing a check my code can't cash.
  • Ben Voigt
    Ben Voigt about 14 years
    Polymorphic use doesn't imply polymorphic deletion. There are plenty of use cases for a class to have virtual methods yet no virtual destructor. Consider a typical statically defined dialog box, in pretty much any GUI toolkit. The parent window will destroy the child objects, and it knows the exact type of each, yet all the child windows will also be used polymorphically in any number of places, such as hit testing, drawing, accessibility APIs that fetch the text for text-to-speech engines, etc.
  • Steve Jessop
    Steve Jessop about 14 years
    True, but the questioner is asking when you should specifically avoid a virtual destructor. For the dialog box you describe, a virtual destructor is pointless, but IMO not harmful. I'm not sure I'd be confident that I'll never need to delete a dialog box using a base class pointer - for example I may in future want my parent window to create its child objects using factories. So it's not a question of avoiding virtual destructor, just that you might not bother having one. A virtual destructor on a class not suitable for derivation is harmful, though, because it's misleading.
  • stoodfarback
    stoodfarback about 14 years
    By saying you have "no intention", you're making a huge assumption about how your class will get used. It seems to me the simplest solution in most cases (which should therefore the default) should be to have virtual destructors, and only avoid them if you have a specific reason not to. So I'm still curious about what would be a good reason.
  • user48956
    user48956 about 13 years
    I agree with mag. This use of a virtual destructor and or virtual method are separate requirements. Virtual destructor provide the ability for a class to perform cleanup (e.g. delete memory, close files, etc...) AND also ensures the constructors of all its members gets called.
  • Luke Maurer
    Luke Maurer over 11 years
    It's not related to the heap exactly, but more to lexical variable scoping. Any local variable's value, when it's the object of a class, is well-known at compile time, so there can't be any confusion over what destructor to call. The values of local variables are exactly the values that are kept on the stack, not the heap; therefore only heap-allocated objects need to be careful of which destructor will be called.
  • kevinarpe
    kevinarpe almost 8 years
    @Windowsprogrammer: So true! How many of us have made the rookie mistake of inheriting from std::vector? Almost always this is a terrible idea, but perfectly encapsulates "should not" vs "cannot / made impossible".
  • John Smith
    John Smith about 7 years
    dear Richard, can you please comment a little bit more on what you have written. I do not understand your point, but it seems the only valuable point that I have found by googling) Or may be you can give a link to a more detailed explanation?
  • Richard Corden
    Richard Corden almost 7 years
    @JohnSmith I've updated the answer. Hopefully this helps.
  • Frank Sebastià
    Frank Sebastià over 6 years
    Please correct me if I'm wrong, but it is said that when any condition applies, but: No instantiation on the heap , you can have a subclass with dynamic memory and the following code will leak resources: Base *b = new Derived(); delete b; see
  • Head Geek
    Head Geek almost 5 years
    Just nit-picking, but these days a pointer will often be 64 bits instead of 32.
  • Andy Borrell
    Andy Borrell almost 5 years
    @FrankSebastià allocation with new is allocation on the heap.
  • csguy
    csguy over 4 years
    "If your code is not performance critical, it would be reasonable to add a virtual destructor to every base class you write, just for safety." should be emphasized more in every answer i see
  • Hans Olsson
    Hans Olsson over 3 years
    @FrankSebastià I see what you mean and the second point should be clarified to: No instantiation on the heap of derived classes.
  • Hans Olsson
    Hans Olsson over 3 years
    @MagRoader In theory you do, but since you only get problem if you store (and delete) a pointer to a derived object allocated on the heap in a pointer to base the obvious question is what use there is of that pointer without virtual functions? I can only see one possibility: you ONLY use the object to delete resources when "done"; and in that case you should have a virtual destructor without any other methods.