Derived Class with Non-Virtual Destructor

Derived class with non-virtual destructor

Are there any circumstances in which it is legitimate for a derived
class to have a non-virtual destructor?

Yes.

A non-virtual destructor signifies that a class should not be used as
a base-class.

Not really; a non-virtual destructor signifies that deleting an instance of derived via a base pointer will not work. For example:

class Base {};
class Derived : public Base {};

Base* b = new Derived;
delete b; // Does not call Derived's destructor!

If you don't do delete in the above manner, then it will be fine. But if that's the case, then you would probably be using composition and not inheritance.

Will having a non-virtual destructor of a derived class act like a
weak form of the Java final modifier?

No, because virtual-ness propagates to derived classes.

class Base
{
public:
virtual ~Base() {}
virtual void Foo() {};
};

class Derived : public Base
{
public:
~Derived() {} // Will also be virtual
void Foo() {}; // Will also be virtual
};

There isn't a built-in language mechanism in C++03 or earlier to prevent subclasses(*). Which isn't much of an issue anyway since you should always prefer composition over inheritance. That is, use inheritance when a "is-a" relationship makes more sense than a true "has-a" relationship.

(*) 'final' modifier was introduced in C++11

Protected non-virtual destructor in the base class

The C++ Standard has this to say about delete (section 5.3.5p10):

Access and ambiguity control are done for both the deallocation function and the destructor (12.4, 12.5).

Therefore, only code that has access to the destructor is able to use delete. Since the destructor is protected, that means that no one can call delete on a pointer of type Base*. Only subclasses can use the destructor at all (and the only thing that will is the subclass's own destructor, as part of the subobject destruction process).

Of course, the subclass should make its own destructor public, allowing you to delete objects through the subclass type (assuming that is the correct actual type).

NOTE: Actually, other members of Base can do delete (Base*)p; since they have access. But C++ assumes that someone using this construct will not be doing that -- C++ access control only provides guidance to code outside your class.

Is it safe to declare non-virtual destructor in the derived class

A function overriding a virtual member function is always virtual, no matter you declare virtual or not. Thus B::~B() is always virtual since A::~A() is virtual.

Class has virtual method but non virtual destructor C++

Classes for use as polymorphic bases should have either a virtual destructor or a protected destructor.

The reason is that if you have a public, non-virtual destructor, then pretty much any use of it by an external user of the class is unsafe. For example:

GenericSymbolGenerator *ptr = new PascalPredefinedSymbolGenerator();
delete ptr; // behavior is undefined, we tried to call the base class destructor

By marking the destructor protected, you prevent the user from deleting a PascalPredefinedSymbolGenerator object via the base class. By making the destructor public and virtual, you get defined behavior when the user deletes through the base class. So pick one of those options.

Is it safe to privately inherit from a class with a non-virtual destructor?

Whether inheritance is public or private does not affect the safety of the code, it just limits the scope in which it can be used safely/unsafely. You have the same basic issue: if your class or a friend of your class passes an object of your type to an interface that takes a pointer to the base without virtual destructor, and if that interface acquires ownership of your object then you are creating undefined behavior.

The problem in the design is that as per your question, the BaseNonVirtual is not designed to be extended. If it was, it should have either a public virtual destructor, or a protected non-virtual one, ensuring that no code will be able to call delete on a derived object through a pointer to the base.

How about a base class with a non-virtual destructor?

You can still say Base * p = ::new Derived; and thus create a dangerous situation.

class has virtual functions and accessible non-virtual destructor

This happens because your base class A does not have a virtual destructor. For instance, if you had this code:

int main()
{
A* a = new B;
delete a;
}

Then the delete a call would not be able to call B's destructor because A's isn't virtual. (It would leak all of B's resources.) You can read more about virtual destructors here.

Add a virtual destructor to your base class and you should be fine.

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

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.



Related Topics



Leave a reply



Submit