When to use virtual destructors?

Viewed 831852

I have a solid understanding of most OOP theory but the one thing that confuses me a lot is virtual destructors.

I thought that the destructor always gets called no matter what and for every object in the chain.

When are you meant to make them virtual and why?

20 Answers

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.

Declare destructors virtual in polymorphic base classes. This is Item 7 in Scott Meyers' Effective C++. Meyers goes on to summarize that if a class has any virtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.

Also be aware that deleting a base class pointer when there is no virtual destructor will result in undefined behavior. Something that I learned just recently:

How should overriding delete in C++ behave?

I've been using C++ for years and I still manage to hang myself.

Make the destructor virtual whenever your class is polymorphic.

If you use shared_ptr(only shared_ptr, not unique_ptr), you don't have to have the base class destructor virtual:

#include <iostream>
#include <memory>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){ // not virtual
        cout << "Base Destructor called\n";
    }
};

class Derived: public Base
{
public:
    Derived(){
        cout << "Derived constructor called\n";
    }
    ~Derived(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    shared_ptr<Base> b(new Derived());
}

output:

Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called

I think most answers here miss the point, with the exception of the accepted one, which is a good thing. However, let me add one more with a different view on the issue: You need a virtual destructor if you want to polymorphically delete instances of this class.

This kind-of sidesteps the question, so let me elaborate: As many pointed out, you get undesired behaviour if you invoke delete base_ptr and the destructor is not virtual. However, there are several assumptions there that need to be make explicit:

  • If your class is not a baseclass, you will hopefully not write code like this. In this case I don't mean the manual memory management, which is bad in and of itself, but deriving publicly from this class. A class not designed as baseclass should not be inherited from, like e.g. std::string. C++ allows you to shoot yourself in the foot. This is your fault though, not that of the baseclass not having a virtual destructor.
  • If the destructor is not accessible (protected or private), this code won't compile, so the undesired behaviour can not occur. Having a protected destructor is useful, especially for mixins but also (to a lesser extent) for interfaces. You don't want to incur the overhead of virtual functions unless you actually make use of them. Making the destructor protected instead prevents undesired behaviour but doesn't restrict you otherwise.
  • If you actually write a class that is supposed to be derived from, you will typically have virtual functions anyways. As a user of them, you will typically only use them via a pointer to the baseclass. When this use includes disposing of them, it then needs to be polymorphic as well. This is then the case when you should make the destructor virtual.

For a similarly different view on the subject, also read When should you not use virtual destructors?

A basic definition about virtual is it determines if a member function of a class can be over-ridden in its derived classes.

A class's D-tor is called basically at the end of the scope, but there is a problem, for example when we define an instance on the Heap (dynamic allocation), we should delete it manually.

As soon as the instruction get executed, the base class destructor get called, but not for the derived one.

A Pratical example is when, in control field, you have to manipulate effectors, actuators.

At the end of the scope, if the destructor of one of the power elements (Actuator), isn't called, there will be fatal consequences.

#include <iostream>

class Mother{

public:

    Mother(){

          std::cout<<"Mother Ctor"<<std::endl;
    }

    virtual~Mother(){

        std::cout<<"Mother D-tor"<<std::endl;
    }


};

class Child: public Mother{

    public:

    Child(){

        std::cout<<"Child C-tor"<<std::endl;
    }

    ~Child(){

         std::cout<<"Child D-tor"<<std::endl;
    }
};

int main()
{

    Mother *c = new Child();
    delete c;

    return 0;
}

Make all destructors virtual unless you have good reason not to.

Otherwise evil like this happens:

Suppose you have an array of Fruit pointers with both Apple and Orange objects.

When you delete from the collection of Fruit objects, ~Apple() and ~Orange() fail to be called unless ~Fruit() is virtual.

Example done right:

#include <iostream>
using namespace std;
struct Fruit { // good
  virtual ~Fruit() { cout << "peel or core should have been tossed" << endl; } 
};
struct Apple:  Fruit { virtual ~Apple()  {cout << "toss core" << endl; } };
struct Orange: Fruit { virtual ~Orange() {cout << "toss peel" << endl; } };

int main() { 
  Fruit *basket[]={ new Apple(), new Orange() };
  for (auto fruit: basket) delete fruit;
};

good output

toss core
peel or core should have been tossed
toss peel
peel or core should have been tossed

Example done wrong:

#include <iostream>
using namespace std;
struct Fruit { // bad 
  ~Fruit() { cout << "peel or core should have been tossed" << endl; } 
};
struct Apple:  Fruit { virtual ~Apple()  {cout << "toss core" << endl; } };
struct Orange: Fruit { virtual ~Orange() {cout << "toss peel" << endl; } };

int main() { 
  Fruit *basket[]={ new Apple(), new Orange() };
  for (auto fruit: basket) delete fruit;
};

bad output

peel or core should have been tossed
peel or core should have been tossed

(Note: Where I used struct for brevity, normally use class and specify public)

I propose this: If a class or struct is not final, you should define virtual destructor for it.

I know this looks like an excessively vigilant overkill to become a rule of thumb. But, it is the only way to be sure that someone deriving from your class won't have UB when deleting with base pointer.

Scott Meyers' recommendation in Effective C++ quoted below is good but not enough to be sure.

if a class has any virtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.

For example, in the program below, base class B does not have any virtual functions, so according to Meyer, you don't need to write a virtual destructor. However, you have UB below, if you don't:

#include <iostream>

struct A
{
    ~A()
    {
        std::cout << "A::~A()" << std::endl;
    }
};

struct B
{
};

struct C : public B
{
    A a;
};

int main(int argc, char *argv[])
{
    B *b = new C;
    delete b; // UB, and won't print "A::~A()"
    return 0;
}
Related