When should you not use virtual destructors?
Asked Answered
P

12

108

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

Petry answered 19/11, 2008 at 4:27 Comment(0)
P
81

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.

Protocol answered 19/11, 2008 at 4:33 Comment(11)
fine overview. i dropped my answer in favour of this. +1 :)Nievelt
This is not a good answer. "There is no need" is different from "should not", and "no intention" is different from "made impossible".Regressive
Also add: no intention to delete an instance via a base class pointer.Featherstone
This doesn't really answer the question. Where is your good reason not to use a virtual dtor?Markettamarkey
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.Protocol
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.Telegraphone
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.Carchemish
@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".Became
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; seeWaynant
@FrankSebastià allocation with new is allocation on the heap.Sprit
@FrankSebastià I see what you mean and the second point should be clarified to: No instantiation on the heap of derived classes.Finance
A
74

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
}
Alongside answered 19/11, 2008 at 15:9 Comment(3)
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.Regressive
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?Monotonous
@JohnSmith I've updated the answer. Hopefully this helps.Alongside
L
30

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.

Latterly answered 19/11, 2008 at 4:37 Comment(4)
And, in fact, there is a warning option on gcc which warns on precisely that case (virtual methods but no virtual dtor).Thanatos
Don't you then run the risk of leaking memory if you derive from the class, regardless of whether you have other virtual functions?Petry
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.Snakebird
@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.Finance
N
7

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.

Nagana answered 19/11, 2008 at 4:54 Comment(1)
"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 seeMithras
M
5

Virtual functions mean every allocated object increases in memory cost by a virtual function table pointer.

So if your program involves allocating a very large number of some object, it would be worth avoiding all virtual functions in order to save the additional 32 bits per object.

In all other cases, you will save yourself debug misery to make the dtor virtual.

Markettamarkey answered 19/11, 2008 at 15:31 Comment(2)
Just nit-picking, but these days a pointer will often be 64 bits instead of 32.Fulcrum
shouldn't this be optimized by the compiler, which knows no class is deriving from class A, so no need of virtual table?Softshoe
H
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.

Hypermeter answered 21/11, 2008 at 14:9 Comment(2)
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.Cephalad
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.Hypermeter
O
4

A good reason for not declaring a destructor as virtual is when this saves your class from having a virtual function table added, and you should avoid that whenever possible.

I know that many people prefer to just always declare destructors as virtual, just to be on the safe side. But if your class does not have any other virtual functions then there is really, really no point in having a virtual destructor. Even if you give your class to other people who then derive other classes from it then they would have no reason to ever call delete on a pointer that was upcast to your class - and if they do then I would consider this a bug.

Okay, there is one single exception, namely if your class is (mis-)used to perform polymorphic deletion of derived objects, but then you - or the other guys - hopefully know that this requires a virtual destructor.

Put another way, if your class has a non-virtual destructor then this is a very clear statement: "Don't use me for deleting derived objects!"

Ossieossietzky answered 17/2, 2016 at 7:12 Comment(0)
T
3

If you have a very small class with a huge number of instances, the overhead of a vtable pointer can make a difference in your program's memory usage. As long as your class doesn't have any other virtual methods, making the destructor non-virtual will save that overhead.

Transmissible answered 12/4, 2010 at 23:19 Comment(0)
E
1

I usually declare the destructor virtual, but if you have performance critical code that is used in an inner loop, you might want to avoid the virtual table lookup. That can be important in some cases, like collision checking. But be careful about how you destroy those objects if you use inheritance, or you will destroy only half of the object.

Note that the virtual table lookup happens for an object if any method on that object is virtual. So no point in removing the virtual specification on a destructor if you have other virtual methods in the class.

Enchanting answered 19/11, 2008 at 10:52 Comment(0)
Y
1

If you absolutely positively must ensure that your class does not have a vtable then you must not have a virtual destructor as well.

This is a rare case, but it does happen.

The most familiar example of a pattern that does this are the DirectX D3DVECTOR and D3DMATRIX classes. These are class methods instead of functions for the syntactic sugar, but the classes intentionally do not have a vtable in order to avoid the function overhead because these classes are specifically used in the inner loop of many high-performance applications.

Yance answered 11/5, 2011 at 22:34 Comment(0)
Z
0

On operation that will be performed on the base class, and that should behave virtually, should be virtual. If deletion can be performed polymorphically through the base class interface, then it must behave virtually and be virtual.

The destructor has no need to be virtual if you don't intend to derive from the class. And even if you do, a protected non-virtual destructor is just as good if deletion of base class pointers isn't required.

Zandra answered 13/11, 2010 at 17:52 Comment(0)
R
-8

The performance answer is the only one I know of which stands a chance of being true. If you've measured and found that de-virtualizing your destructors really speeds things up, then you've probably got other things in that class that need speeding up too, but at this point there are more important considerations. Some day someone is going to discover that your code would provide a nice base class for them and save them a week's work. You'd better make sure they do that week's work, copying and pasting your code, instead of using your code as a base. You'd better make sure you make some of your important methods private so that no one can ever inherit from you.

Regressive answered 19/11, 2008 at 5:4 Comment(3)
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.Protocol
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.Hypermeter
... 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.Hypermeter

© 2022 - 2024 — McMap. All rights reserved.