3

This is from Effective C++ (Meyers):

Classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors

I don't understand why non-polymorphic classes should not declare virtual destructors.

Assuming I have a parent class and a child class, with no virtual functions, and I have a parent-class-pointer to a child object: if I call delete on the parent-class-pointer, it will only call the parent destructor, even though I also want to call the child destructor.

3
  • 4
    "if I call delete on the parent-class-pointer, it will only call the parent destructor, even though I also want to call the child destructor." - Yeah that practically is using the class polymorphically then, so given the above rule it actually should have a virtual destructor. Everything fine then. Jan 12, 2016 at 18:35
  • 5
    "I have a parent class and a child class, with no virtual functions, and I have a parent-class-pointer to a child object" - But why would you want that? The only thing you could do with such a thing is destroy it (or use dynamic_cast). Jan 12, 2016 at 18:36
  • @SebastianRedl Or typeid!
    – curiousguy
    Nov 22, 2018 at 6:49

3 Answers 3

5

The keyword here is not designed

Classes not designed to be base classes or not designed to be used polymorphically

Virtual functions/destructors are not free: they cause performance overhead so you may not want to use them in all cases. However, deleting a derived class object using a pointer to a base class that has a non-virtual destructor results in undefined behavior:

http://www.geeksforgeeks.org/g-fact-37/

So that's the trade off between performance and safety.

5

Assuming I have a parent class and a child class, with no virtual functions, and I have a parent-class-pointer to a child object

Then you are using it polymorphically.

Though I don't see much point in doing that. Without virtual members, the subclass does not have much opportunity to affect the program behaviour compared to using just the base class.

A non-polymorphic class is one that will not be used to point to objects of derived classes. These are either classes that won't be derived (which in C++ is vast majority!) or classes that are used as base for purpose of code reuse without providing complete interface for anything (e.g. std::iterator; it is a public base class of most iterators, but it makes no sense to create a std::iterator *).

1

As a concrete example, assume something like this:

class Base
{
    ...
    int some_int;
};

class Derived: public Base
{
    ...
    vector<int> some_vec; // the presence of this field makes `Derived` 
                          // no longer trivially destructible.
};

Now if we do this (either directly as shown or indirectly through a smart pointer, e.g.):

Base* base = new Derived;
...
delete base;

... then some_vec will not be destroyed unless Base defines a virtual destructor. After all, given only a base pointer, the system lacks the information at runtime to know anything about Derived unless there's a virtual table that points to its functions, allowing a dynamic dispatch to occur to get to information/functionality specific to Derived like its specific requirements for proper destruction.

To delete an object through a base pointer is polymorphic behavior, and to do it safely and correctly without running into undefined behavior requires the base class to define a virtual destructor.

The question ultimately boils down to whether you want to delete objects through a base pointer. If you do, then define a public virtual destructor:

class BaseSafeDelete
{
public:
    // Provides safe destruction through a base pointer.
    virtual ~BaseSafeDelete() {}
};

If not, define a protected nonvirtual destructor:

class BaseNoDelete
{
protected:
    // Prevents deleting outright through a base pointer.
    /*nonvirtual*/ ~BaseNoDelete() {}
};

And most importantly, inheritance is something to be designed and decided upfront when designing a base class. It's not suitable as an afterthought to take a class which was not designed for inheritance whatsoever (lacking either a protected nonvirtual dtor or a public virtual dtor as a first sign) and attempt to extend it. For that, see the Composite Reuse Principle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.