Virtual destructor: is it required when not dynamically allocated memory?
Asked Answered
O

6

22

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 ?

Orestes answered 14/1, 2010 at 17:2 Comment(2)
@ALL I executed above code sample with valgrind, by creating derived class object dynamically (i.e. "A * a = new B;delete a;") I observed there is no memory leak. Even derived class destructor is not invoked. Why not memory leak?Ocean
@Ocean same deal with my tests. For some reason valgrind does not report memory leaks. I think it should.Erlene
R
33

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.

Rimple answered 14/1, 2010 at 17:6 Comment(7)
Although the answer is correct -- you always go to the standard -- without providing any descent explanation -- the answers should teach new programmers not to preach to (or validate the knowledge of) experienced ones.Coycoyle
The Standard doesn't say WHY it undefined behaviour, any more than it does for divide by zero.Rimple
@Autopopulated People have some very strange ideas about this one. anyone would think adding a virtual destructor added some vast overhead. given the rationalistions they will produce to avoid doing so.Rimple
@Autopulated: Are you sure you can recognize a right answer when you see one?Sorilda
I actually don't think this is undefined - I think it is implicitly defined that A::~A (and only A::~A) will be called. This is pretty much never what you want. Where do you see this as undefined?Carpology
It's undefined because the C++ Standard says it is.Rimple
Terry: anything not explicitly specified in the standard or anything stated by the standard to be undefined is undefined. If you do something that is undefined, the compiler is free to do whatever it wants. In this case, it states that its undefined. "Nevertheless, there is something very troubling here. Your program's behavior is undefined -- you have no way of knowing what will happen...That means compilers may generate code to do whatever they like: reformat your disk, send suggestive email to your boss, fax source code to your competitors, whatever." -- Scott Meyers, "Effective C++"Troop
S
21

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.

Sorilda answered 14/1, 2010 at 17:10 Comment(4)
AndreyT: so classes that have no resources but can form polymorphic hierarchies should always define empty virtual destructors?Massacre
@Eli Bendersky: Yes, exactly. Except that, of course, 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.Sorilda
Great job explaining the details.Detachment
Although it is not necessary to add "virtual" when overloading a virtual fonction, I usually do so, just to keep this information in the immediate context and no force the reader (well, me :x) to trample to the base class to see if it is effectively virtual.Flagellum
E
4

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?

Evergreen answered 14/1, 2010 at 17:5 Comment(6)
(+1) Guess someone is on their usual down-voting spree again. The answer adds value.Coycoyle
@Autopulated yes Neil is right. But there can be more than one correct answer. Mine is certainly not incorrect.Evergreen
Somebody downvoted me too - just to be even-handed, I suppose :-)Rimple
"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?" this isn't correct. If you don't intend for users of your class to use it polymorphically, or off the heap, and you have no virtual functions, then there is sometimes no need to add a virtual destructor. Why do you think std::unary_function has no virtual destructor?Warwick
@Brian, i should have been clearer. I meant this statement to only apply to scenarios where inheritance was intended.Evergreen
And inheritance should only ever be used (say the OO-gods) when true polymorphism is needed... unfortunately C++ does not have any concept of delegation, so inheritance is also often used just to get typedefs / methods... because it's just so damn boring to actually write forwarding for everything -_-Flagellum
D
0

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.

Dionysian answered 14/1, 2010 at 17:7 Comment(2)
You did not get the purpose of a virtual destructor. It has nothing to do with what your base class do and everything to do with how others (over which you have no control) use your class.Flagellum
Aha. Maybe I didn't explain long enough... and I haven't separated discussion of virtual dtor from general presence of a non-trivial dtor. I know I don't have control over what others do with my class, but I can express some intent. A virtual destructor (or other virtual functions) tells you that you are expected to derive from that class. If a virtual dtor is missing, it might be because deriving from that class is not intended, at least not in such a way that the derived classes shall be destructed using a pointer to the base class.Dionysian
C
0

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.

Coppola answered 14/1, 2010 at 17:43 Comment(1)
It is in fact more stringent than that. Of course we all agree that in this case only A::~A() is called instead of B::~B() if the delete operator somehow use the size information of the type to know how much should be freed, what will happen ? delete is implementation defined, and this undefined behavior, so no one knows, apart from reverse engineering (or readings the specs) of a given implementation.Flagellum
C
-1

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.

Chilli answered 6/9, 2012 at 4:23 Comment(1)
Could you elaborate on your reference?Hawsepipe

© 2022 - 2024 — McMap. All rights reserved.