When should you not use virtual destructors?
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 delete
ing 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.
Related videos on Youtube
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, 2021Comments
-
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 over 15 yearsfine overview. i dropped my answer in favour of this. +1 :)
-
Windows programmer over 15 yearsThis is not a good answer. "There is no need" is different from "should not", and "no intention" is different from "made impossible".
-
CesarB over 15 yearsAnd, in fact, there is a warning option on gcc which warns on precisely that case (virtual methods but no virtual dtor).
-
Adam Rosenfield over 15 yearsAlso add: no intention to delete an instance via a base class pointer.
-
mxcl over 15 yearsThis doesn't really answer the question. Where is your good reason not to use a virtual dtor?
-
Mag Roader over 15 yearsDon't you then run the risk of leaking memory if you derive from the class, regardless of whether you have other virtual functions?
-
sep over 15 yearsI 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 over 15 yearsPolymorphism 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 over 15 yearsYou'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 over 15 yearsIn 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 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 about 14 yearsPolymorphic 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 about 14 yearsTrue, 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 about 14 yearsBy 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 about 13 yearsI 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 over 11 yearsIt'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 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 about 7 yearsdear 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 almost 7 years@JohnSmith I've updated the answer. Hopefully this helps.
-
Frank Sebastià over 6 yearsPlease 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 almost 5 yearsJust nit-picking, but these days a pointer will often be 64 bits instead of 32.
-
Andy Borrell almost 5 years@FrankSebastià allocation with new is allocation on the heap.
-
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 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 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.