Why Should I Declare a Virtual Destructor For an Abstract Class in C++

Why should I declare a virtual destructor for an abstract class in C++?

It's even more important for an interface. Any user of your class will probably hold a pointer to the interface, not a pointer to the concrete implementation. When they come to delete it, if the destructor is non-virtual, they will call the interface's destructor (or the compiler-provided default, if you didn't specify one), not the derived class's destructor. Instant memory leak.

For example

class Interface
{
virtual void doSomething() = 0;
};

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

void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}

virtual destructor for pure abstract class

My question is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members?

It depends. If you have a case like

base * foo = new child(stuff);
// doing stuff
delete foo;

then you absolutely must have a virtual destructor. Without it you'll never destroy the child part.

If you have a case like

child * foo = new child(stuff);
// doing stuff
delete foo;

Then you do not need a virtual destructor, as child's will be called.

So the rule is if you delete polymorphically, you need a polymorphic (virtual) destructor, if not, then you don't

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.

Abstract class object and pure virtual destructor

We cannot make the object of the abstract class

This means that an abstract class cannot be instantiated by itself. It does not mean that objects of abstract class cannot be instantiated as part of a derived object, though: an instance of any derived class is also an instance of its abstract base.

This is where the destructor comes in: if you need one to free private resources allocated in the constructor of the abstract class, the only place to put the clean-up is its destructor.

Is it necessary to make the destruct pure virtual for an abstract class?

It is not necessary to mark the destructor pure virtual. You do it only if you define no other functions that could be marked pure virtual. In any event, you must provide an implementation for the destructor, even if you mark it pure virtual.

C++ abstract class destructor

Having only a pure virtual destructor in the base class is rarely a good practice, but it is possible and, in certain cases, even desirable.

For instance, if you rely on RTTI to dispatch messages to a subclass object by attempting a dynamic_cast on the pointer to the base class, you may need no methods in the base class except the destructor. In this case, make the destructor public virtual.

Since you have no other virtual methods except the destructor, you have to make it pure in order to prevent the creation of the objects of the base class.

Here, it is crucial to provide the empty body to that destructor (even though it is "=0"!)

struct AbstractBase {
virtual ~AbstractBase() = 0;
}

AbstractBase::~AbstractBase() {
}

Having the body allows the creation of subclass objects (provided that the subclass defines the destructor and does not set it as pure).

Virtual destructor for pure abstract base classes

Whereas, clang generates warning for:

class A {
public:
virtual ~A() {}
virtual void foo() = 0;
};

using = default doesn't trigger it.

class A {
public:
virtual ~A() = default;
virtual void foo() = 0;
};

Even if both are valid.

Demo

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.
};

C++ Should an abstract class provide implementation for a (virtual) destructor?

You must provide a definition of every destructor in a class hierarchy, since all destructors up the inheritance graph do get called when a derived class is destroyed, even if some of the destructors are trivial.

Typically, you achieve this by putting virtual ~Base() { } (or some exception-specified variation thereof) in your base class. However, if the destructor is the only virtual member function and you want to make the base abstract, you can make the destructor pure-virtual, but you still have to define it:

struct Base
{
virtual ~Base() = 0;
};

Base::~Base() { }

Pure virtual destructor of a local abstract class

cppreference says

A class declaration can appear (...) and inside the body of a function, in which case it defines a local class

...

Member functions of a local class have to be defined entirely inside the class body

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.



Related Topics



Leave a reply



Submit