views:

279

answers:

4

Destructors aren't virtual by default to not hurt when its not needed, which is fine.

But in case of a base class derived class scenario, is there any use case for not having a virtual destructor? If not could it be possible (does it make sense) for the compiler to complain if a class derives from a base class which has a public non virtual destructor (or no destructor) defined. And not just warn about it.

+7  A: 

The problem with your idea is that it's conceivable that someone is using a non-virtual base class destructor as an optimization (if you're never going to destroy via a base-class pointer, then the missing virtual won't hurt you, and still avoids the vtable entry).

Since it COULD be used, it's allowed. I'd think an optional compiler warning might be a good idea, but not something in the language spec.

Michael Kohne
This is basically the only reason you don't want a virtual destructor in a base class. In embedded situations taking on the risk and responsibility of a non-virtual destructor is worth the memory cost.
Nick Bastin
@Michael Kohne: Then you should make the destructor `protected` so that someone can't inadvertently destroy using a base class pointer.
Billy ONeal
Take boost::noncopyable, for example. It's meant to be inherited from, but you're never ever going to cast to that base class and delete, so it doesn't need a virtual dtor. And having one would force an awful lot of classes to include vtables which could otherwise be avoided
jalf
Aside from possible overheads you are losing POD-ness, which means that e.g. aggregate initialization is no longer possible.
Georg Fritzsche
thank you everyone!
neal aise
@Billy ONeal: probably a good plan in most cases.
Michael Kohne
+6  A: 

Because it's perfectly valid to have a non-virtual destructor. If sub classes are only designed to be stack allocated, for example, there's no need for a virtual destructor. Why require clients to have all the vtbl machinery when a class is only supposed to be a decorator?

It also makes little sense to have a virtual destructor when a class is meant to be inherited from privately (implemented-in-terms-of).

This all said, a destructor should usually be public and virtual or protected, unless a class is not meant to be a base class.

Billy ONeal
"are only designed to be stack allocated". It is only problematic when you call `delete` on a polymorphic object from a `base` pointer. i.e. it is fine to allocate the derived class on the heap, even if the base's destructor is non-virtual, as long as you delete the object from pointer to the derived class but not the base.
AraK
@AraK: Then you make the destructor `protected` so that they can't do that.
Billy ONeal
+5  A: 

Virtual destructor is only needed if you perform polymorphic destruction of the object through delete. That, in turn, immediately implies dynamically allocated (new-ed) objects.

If you don't allocate objects dynamically, you don't need the virtual destructor. This immediately offers an unlimited source of use-cases when the virtual destructor in base class is not necessary.

If you allocate objects dynamically, but never destroy them polymorphically, you don't need the virtual destructor. This adds another set of use-cases when the virtual destrcutor in base class is not necessary.

AndreyT
+2  A: 

There are different issues with what you propose, the first of which is already dealt in other answers: you only need virtual destructors if you intend to delete through a pointer to a base (a general recommendation is to provide either a public virtual destructor or a protected non-virtual destructor as that would inhibit deletion through the base class).

There is another issue in that when a compiler sees a class definition it cannot possibly know whether it is going to be derived or not. Consider if you implement a base class in a translation unit. At a later time you derive from the class. If that derivation would imply making the constructor virtual the base class translation would have to be recompiled, or else the ODR (One Definition Rule) would be broken in your program.

If you add other translation units to the mix, things get even worse. Whenever you include a header file from a translation unit you would be forced to also manually include at least one header where a derived object from that class is defined (increasing coupling), or else, again, the compiler would generate a different definition for that single class in that translation unit (compared to the translation unit where the derived class is defined) breaking the ODR again.

The problem is that the compiler only has a partial view of your project, and cannot really infer what you need/want from what it sees.

David Rodríguez - dribeas