Should Every Class Have a Virtual Destructor

Should every class have a virtual destructor?

The question is really, do you want to enforce rules about how your classes should be used? Why?
If a class doesn't have a virtual destructor, anyone using the class knows that it is not intended to be derived from, and what limitations apply if you try it anyway. Isn't that good enough?

Or do you need the compiler to throw a hard error if anyone dares to do something you hadn't anticipated?

Give the class a virtual destructor if you intend for people to derive from it. Otherwise don't, and assume that anyone using your code is intelligent enough to use your code correctly.

When to use virtual destructors?

Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:

class Base 
{
// some virtual methods
};

class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};

Here, you'll notice that I didn't declare Base's destructor to be virtual. Now, let's have a look at the following snippet:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Since Base's destructor is not virtual and b is a Base* pointing to a Derived object, delete b has undefined behaviour:

[In delete b], if the static type of the
object to be deleted is different from its dynamic type, the static
type shall be a base class of the dynamic type of the object to be
deleted and the static type shall have a virtual destructor or the
behavior is undefined
.

In most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.

To sum up, always make base classes' destructors virtual when they're meant to be manipulated polymorphically.

If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete on a base class pointer.

You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.

Is it always necessary to declare destructor as virtual, if the class contains atleast a virtual function?

A virtual destructor ensures that the inherited class destructor is called when you have a pointer to a base class.

In this particular case you don't need it, but a user could inherite from der another class (let it be foo) which uses -for example- dynamic memory allocation. In that case the destructor wouldn't be called unless he has a pointer of type foo.

So no, it's not "necessary" but if you already have at least a virtual function (hence you already have a VTABLE) there is no harm either. It's mandatory if you assume that those class are to be inherited by the user and are freed using a pointer to the base class.

When should you not use virtual destructors?

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.

Do I still need to have a virtual destructor for my derived class in this situation?

It already is!

The virtual keyword is implicit for your derived::someFunc, and for your derived::~derived, because both functions' base equivalents are marked virtual.

So, you may write the keyword virtual on both, neither or just one. It doesn't matter: they will both be actually virtual regardless.

Should the destructor be always virtual when a class is part of a larger framework?

As @Konrad Grochowski's answer states, the destructor of B is implictly virtual, so the behavior is well defined. In your example, C::~C() will be called. From the C++ spec § 12.4.8:

A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.

The more pertinent question, is what happens when your framework base class actually has a non-virtual destructor (which I think is what you were driving at), and your user derives from it. For example:

// Your framework:
class A
{
public:
A();
~A(); // non-virtual
};

// User's class:
class B : public A
{
B();
virtual ~B(); // virtual
virtual void UserMethod();
};

As discussed in this question (Non virtual destructor in base class, but virtual destructor in derived class cause segmentation fault), this could cause your users to run into problems. If you unsure whether the user will derive from your class, it should have a virtual destructor, otherwise there is a potential for problems.

To enforce proper behavior with non-virtual destructors, you could disallow the user from deriving from the class, in which case a non-virtual destructor could be safe, assuming you use the class properly within your framework. In C++11, you can use final to disallow derivation. In C++03 and below, you can use the trick here to disallow derivation.

Default to making classes either `final` or give them a virtual destructor?

The probably most common actual issue attributed to the lack of a virtual destructor is deletion of an object through a pointer to a base class:

struct Base { ~Base(); };
struct Derived : Base { ~Derived(); };

Base* b = new Derived();
delete b; // Undefined Behaviour

A virtual destructor also affects the selection of a deallocation function. The existence of a vtable also influences type_id and dynamic_cast.

If your class isn't use in those ways, there's no need for a virtual destructor. Note that this usage is not a property of a type, neither of type Base nor of type Derived. Inheritance makes such an error possible, while only using an implicit conversion. (With explicit conversions such as reinterpret_cast, similar problems are possible without inheritance.)

By using smart pointers, you can prevent this particular problem in many cases: unique_ptr-like types can restrict conversions to a base class for base classes with a virtual destructor (*). shared_ptr-like types can store a deleter suitable for deleting a shared_ptr<A> that points to a B even without virtual destructors.

(*) Although the current specification of std::unique_ptr doesn't contain such a check for the converting constructor template, it was restrained in an earlier draft, see LWG 854. Proposal N3974 introduces the checked_delete deleter, which also requires a virtual dtor for derived-to-base conversions. Basically, the idea is that you prevent conversions such as:

unique_checked_ptr<Base> p(new Derived); // error

unique_checked_ptr<Derived> d(new Derived); // fine
unique_checked_ptr<Base> b( std::move(d) ); // error

As N3974 suggests, this is a simple library extension; you can write your own version of checked_delete and combine it with std::unique_ptr.


Both suggestions in the OP can have performance drawbacks:

  1. Mark a class as final

This prevents the Empty Base Optimization. If you have an empty class, its size must still be >= 1 byte. As a data member, it therefore occupies space. However, as a base class, it is allowed not to occupy a distinct region of memory of objects of the derived type. This is used e.g. to store allocators in StdLib containers.
C++20 has mitigated this with the introduction of [[no_unique_address]].


  1. Have a virtual destructor

If the class doesn't already have a vtable, this introduces a vtable per class plus a vptr per object (if the compiler cannot eliminate it entirely). Destruction of objects can become more expensive, which can have an impact e.g. because it's no longer trivially destructible. Additionally, this prevents certain operations and restricts what can be done with that type: The lifetime of an object and its properties are linked to certain properties of the type such as trivially destructible.


final prevents extensions of a class via inheritance. While inheritance is typically one of the worst ways to extend an existing type (compared to free functions and aggregation), there are cases where inheritance is the most adequate solution. final restricts what can be done with the type; there should be a very compelling and fundamental reason why I should do that. One cannot typically imagine the ways others want to use your type.

T.C. points out an example from the StdLib: deriving from std::true_type and similarly, deriving from std::integral_constant (e.g. the placeholders). In metaprogramming, we're typically not concerned with polymorphism and dynamic storage duration. Public inheritance often just the simplest way to implement metafunctions. I do not know of any case where objects of metafunction type are allocated dynamically. If those objects are created at all, it's typically for tag dispatching, where you'd use temporaries.


As an alternative, I'd suggest using a static analyser tool. Whenever you derive publicly from a class without a virtual destructor, you could raise a warning of some sort. Note that there are various cases where you'd still want to derive publicly from some base class without a virtual destructor; e.g. DRY or simply separation of concerns. In those cases, the static analyser can typically be adjusted via comments or pragmas to ignore this occurrence of deriving from a class w/o virtual dtor. Of course, there need to be exceptions for external libraries such as the C++ Standard Library.

Even better, but more complicated is analysing when an object of class A w/o virtual dtor is deleted, where class B inherits from class A (the actual source of UB). This check is probably not reliable, though: The deletion can happen in a Translation Unit different to the TU where B is defined (to derive from A). They can even be in separate libraries.

When should your destructor be virtual?

  1. You need virtual destructor when at
    least one of class methods is
    virtual.

This is because the reason for virtual method is that you want to use polymorphism. Meaning you will call a method on the base class pointer and you want the most derived implementation - this is the whole point of polymorphism.

Now if you did not have virtual destructor and through the pointer to base class you call destructor you end up calling base class destructor. In this case you want polymorphism to work on your destructor as well, e.g. through calling destructor on your base class you want to end up calling destructor of your most derived class not your base class.

class A
{
virtual void f() {}
~A() {}
}

class B : public A
{
void f() {}
~B() {}
}

A * thing = new B();
thing->f(); // calls B's f()
delete thing; // calls ~A(), not what you wanted, you wanted ~B()

having ~A() virtual turns on polymorphism

virtual ~A() {}

So when you now call

delete thing;

~B() will be called.

You would declare virtual destructors when you design class as an interface e.g. you expect it to be extended or implemented. A good practice in that case is to have a interface class (in the sense of Java interfaces) with virtual methods and virtual destructor and then have concrete implementation classes.

You can see that STL classes don't have virtual destructors so they are not supposed to be extended (e.g. std::vector, std::string ...). If you extend std::vector and you call destructor on base class via pointer or reference you will definitely not call your specialized class destructor which may lead to memory leaks.



Related Topics



Leave a reply



Submit