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 Answers 3
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.
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 *
).
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.
typeid
!