When to Use Virtual Destructors

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.

When should your destructor be virtual? [duplicate]

  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.

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.

Why do we need a pure virtual destructor in C++?

  1. Probably the real reason that pure virtual destructors are allowed is that to prohibit them would mean adding another rule to the language and there's no need for this rule since no ill-effects can come from allowing a pure virtual destructor.

  2. Nope, plain old virtual is enough.

If you create an object with default implementations for its virtual methods and want to make it abstract without forcing anyone to override any specific method, you can make the destructor pure virtual. I don't see much point in it but it's possible.

Note that since the compiler will generate an implicit destructor for derived classes, if the class's author does not do so, any derived classes will not be abstract. Therefore having the pure virtual destructor in the base class will not make any difference for the derived classes. It will only make the base class abstract (thanks for @kappa's comment).

One may also assume that every deriving class would probably need to have specific clean-up code and use the pure virtual destructor as a reminder to write one but this seems contrived (and unenforced).

Note: The destructor is the only method that even if it is pure virtual has to have an implementation in order to instantiate derived classes (yes pure virtual functions can have implementations).

struct foo {
virtual void bar() = 0;
};

void foo::bar() { /* default implementation */ }

class foof : public foo {
void bar() { foo::bar(); } // have to explicitly call default implementation.
};

No virtual functions, but still need virtual destructor?

There is no polymorphism because you call (will call) non-virtual functions using the referemce. That is in your example you simply call (will call) functions of the base class.

Moreover this statement

base& baseVar = derived();

should not be compiled because you bind a temporary object with a non-const reference.

There must be

const base& baseVar = derived();

As for the destructor in your example then there is no need to have a virtual destructor. Becasue you defined a reference to a temporary object of the derived class. In this case for the temporary object will be called its own destructor. That is at first the destructor of the base class will be called (from the destructor of the derived class) and then the body of the destructor of the derived class will be executed.

The vertual destructor would be need if you would allocate a derived memory in the heap and assign the address of the allocated memory to a pointer of the base class. And when you would call operator delete for this pointer then if you have no virtual destructor then the only destructor of the base class would be called.

For example

class Base;
class Derived;

main()
{
base* baseVar = new derived();
delete baseVar; // base shall have a virtual destructor
}

Understanding virtual destructors

First, a little about the difference between virtual functions and non-virtual functions:

Every non-virtual function-call that you have in your code can be resolved during compilation or linkage.

By resolved, we mean that the address of the function can be computed by the compiler or the linker.

So in the object code created, the function-call can be replaced with an op-code for jumping to the address of that function in memory.

With virtual functions, you have the ability to invoke functions which can be resolved only during runtime.

Instead of explaining it, let's run through a simple scenario:

class Animal
{
virtual void Eat(int amount) = 0;
};

class Lion : public Animal
{
virtual void Eat(int amount) { ... }
};

class Tiger : public Animal
{
virtual void Eat(int amount) { ... }
};

class Tigon : public Animal
{
virtual void Eat(int amount) { ... }
};

class Liger : public Animal
{
virtual void Eat(int amount) { ... }
};

void Safari(Animal* animals[], int numOfAnimals, int amount)
{
for (int i=0; i<numOfAnimals; i++)
animals[i]->Eat(amount);
// A different function may execute at each iteration
}

As you can probably understand, the Safari function allows you to be flexible and feed different animals.

But since the exact type of each animal is not known until runtime, so is the exact Eat function to be called.


The constructor of a class cannot be virtual because:

Calling a virtual function of an object is performed through the V-Table of the object's class.

Every object holds a pointer to the V-Table of its class, but this pointer is initialized only at runtime, when the object is created.

In other words, this pointer is initialized only when the constructor is called, and therefore the constructor itself cannot be virtual.

Besides that, there is no sense for the constructor to be virtual in the first place.

The idea behind virtual functions is that you can call them without knowing the exact type of the object with which they are called.

When you create an object (i.e., when you implicitly call a constructor), you know exactly what type of object you are creating, so you have no need for this mechanism.


The destructor of a base-class has to be virtual because:

When you statically allocate an object whose class inherits from the base-class, then at the end of the function (if the object is local) or the program (if the object is global), the destructor of the class is automatically invoked, and in turn, invokes the destructor of the base-class.

In this case, there is no meaning to the fact that the destructor is virtual.

On the other hand, when you dynamically allocate (new) an object whose class inherits from the base-class, then you need to dynamically deallocate (delete) it at some later point in the execution of the program.

The delete operator takes a pointer to the object, where the pointer's type may be the base-class itself.

In such case, if the destructor is virtual, then the delete operator invokes the destructor of the class, which in turn invokes the destructor of the base-class.

But if the destructor is not virtual, then the delete operator invokes the destructor of the base-class, and the destructor of the actual class is never invoked.

Consider the following example:

class A
{
A() {...}
~A() {...}
};

class B: public A
{
B() {...}
~B() {...}
};

void func()
{
A* b = new B(); // must invoke the destructor of class 'B' at some later point
...
delete b; // the destructor of class 'B' is never invoked
}

Should I default virtual destructors?

Yes, you can definitely use = default for such destructors. Especially if you were just going to replace it with {}. I think the = default one is nicer, because it's more explicit, so it immediately catches the eye and leaves no room for doubt.

However, here are a couple of notes to take into consideration when doing so.

When you = default a destructor in the header file (see edit) (or any other special function for that matter), it's basically defining it in the header. When designing a shared library, you might want to explicitly have the destructor provided only by the library rather than in the header, so that you could change it more easily in the future without requiring a rebuild of the dependent binary. But again, that's for when the question isn't simply whether to = default or to {}.


EDIT: As Sean keenly noted in the comments, you can also use = default outside of the class declaration, which gets the best of both worlds here.


The other crucial technical difference, is that the standard says that an explicitly defaulted function that can't be generated, will simply not be generated. Consider the following example:

struct A { ~A() = delete; };
struct B : A { ~B() {}; }

This would not compile, since you're forcing the compiler to generate the specified code (and its implicit requisites, such as calling A's destructor) for B's destructor--and it can't, because A's destructor is deleted. Consider this, however:

struct A { ~A() = delete; };
struct B : A { ~B() = default; }

This, in fact, would compile, because the compiler sees that ~B() cannot be generated, so it simply doesn't generate it--and declares it as deleted. This means that you'll only get the error when you're trying to actually use/call B::~B().

This has at least two implications which you should be aware of:

  1. If you're looking to get the error simply when compiling anything that includes the class declaration, you won't get it, since it's technically valid.
  2. If you initially always use = default for such destructors, then you won't have to worry about your super class's destructor being deleted, if it turns out it's ok and you never really use it. It's kind of an exotic use, but to that extent it's more correct and future-proof. You'll only get the error if you really use the destructor--otherwise, you'll be left alone.

So if you're going for a defensive programming approach, you might want to consider simply using {}. Otherwise, you're probably better off = defaulting, since that better adheres to taking the minimum programmatic instructions necessary to get a correct, working codebase, and avoiding unintended consequences1.


As for = 0: Yes, that's still the right way to do it. But please note that it in fact does not specify that there's "no default implementation," but rather, that (1) The class is not instantiatable; and (2) Any derived classes must override that function (though they can use an optional implementation provided by the super class). In other words, you can both define a function, and declare it as pure virtual. Here's an example:

struct A { virtual ~A() = 0; }
A::~A() = default;

This will ensure these constraints on A (and its destructor).


1) A good example of why this can be useful in unexpected ways, is how some people always used return with parentheses, and then C++14 added decltype(auto) which essentially created a technical difference between using it with and without parentheses.

Why is destructor always declared virtual [duplicate]

It's declared virtual so that inheriting classes will remember to override this method to do their own cleanup. This is important to prevent memory leaks.



Related Topics



Leave a reply



Submit