Do we need a virtual destructor if my classes do not allocate any memory dynamically ?
e.g.
class A
{
private:
int a;
int b;
public:
A();
~A();
};
class B: public A
{
private:
int c;
int d;
public:
B();
~B();
};
In this case do we need to mark A's destructor as virtual ?
The issue is not whether your classes allocate memory dynamically. It is if a user of the classes allocates a B object via an A pointer and then deletes it:
A * a = new B;
delete a;
In this case, if there is no virtual destructor for A, the C++ Standard says that your program exhibits undefined behaviour. This is not a good thing.
This behaviour is specified in section 5.3.5/3 of the Standard (here referring to delete
):
if the static type of the operand is
different from its dynamic type, the
static type shall be a base class of
the operand’s dynamic type and the
static type shall have a virtual
destructor or the behavior is
undefined.
The purpose of virtual destructor (i.e. the purpose of making a destructor virtual) is to facilitate the polymorphic deletion of objects through delete-expression. If your design does not call for polymorphic deletion of objects, you don't need virtual destructors. Referring to your example, if you'll ever have to delete an object of type B
through a pointer of type A *
(polymorphic deletion), you'll need virtual destructor as high up in the hierarchy as A
. That's how it looks from a formal point of view.
(Note, BTW, as Neil said, that what's important is how you create/delete your class objects, not how classes manage their internal memory.)
As for the good programming practices... It depends on your intent and your design in the end. If your classes are not designed to be polymorphic at all (no virtual methods whatsoever), then you don't need virtual destructors. If your class is polymorphic (have at least one virtual method), then making the destructor virtual "just in case" might be a very good idea, and in this case it bears virtually zero performance/memory penalty with it.
The latter is usually expressed as a rather well-known good practice guideline: if your class has at least one virtual method, make the destructor virtual as well. Although from the formal point of view a virtual destructor might not be really needed there, it is still a pretty good guideline to follow.
Classes that have no resources but can form polymorphic hierarchies should always define empty virtual destructors, except that it is perfectly sufficient to define an explicit empty (and even pure) virtual destructor at the very base of the hierarchy. All other destructors will become virtual automatically, even if they are defined implictly by the compiler. I.e. you don't have to explicitly define an empty destructor in every class. Just the base is enough.
Freeing memory is not the only critical function a destructor can perform. It can also be used to reset global state for instance. Not doing this won't leak memory but could potentially cause other issues in your program.
Additionally, even if your destructor doesn't do anything useful today, it may at some point in the future. There's no real reason to avoid a virtual destructor if you have inheritance so why not just add it and sleep better at night?
The destructor of the parent class is always automatically called, and the default dtor is always generated if there's no explicit dtor declared. In your example, neither A nor B needs to have a non-trivial dtor.
If you class has virtual functions, an additional virtual dtor doesn't hurt, and is good practice. In case you class allocates memory or any other resource (like opening a file), a dtor is needed to free that resource again upon destruction.
The purpose of declaring destructor as virtual is to be able to invoke the derived class's destructor whenever you call delete on a pointer of type Base which is pointing to object of type Derived. Not doing so would result in undefined behavior.
The assumption that you need not mark destructor as virtual if you are not allocating memory dynamically implies that you do not need to call derived class destructor if you are not allocating memory dynamically, which is wrong. As you may still do several other operations in your derived class's destructor other than just deallocating the dynamically allocated memory. Examples would be closing an open file, logging some information etc.
If your only concern is memory, maybe you should start by protecting base class destructor (and/or maybe others). Then if something does not compile, you'll see why. Ref: boost::any ways.