6

Few hours back I was fiddling with a Memory Leak issue and it turned out that I really got some basic stuff about virtual destructors wrong! Let me put explain my class design.

class Base
{
  virtual push_elements()
  {}
};

class Derived:public Base
{
vector<int> x;
public:
   void push_elements(){ 
      for(int i=0;i <5;i++)
         x.push_back(i); 
   }
};

void main()
{
    Base* b = new Derived();
    b->push_elements();
    delete b;
}

The bounds checker tool reported a memory leak in the derived class vector. And I figured out that the destructor is not virtual and the derived class destructor is not called. And it surprisingly got fixed when I made the destructor virtual. Isn't the vector deallocated automatically even if the derived class destructor is not called? Is that a quirk in BoundsChecker tool or is my understanding of virtual destructor wrong?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Prabhu
  • 3,434
  • 8
  • 40
  • 48
  • 1
    Please don't format your code using HTML. Select in and press the `0101` button which will indent it by 4 spaces. – Yacoby Apr 27 '10 at 15:31
  • the code as posted has no relationship between Base and Derived – JRL Apr 27 '10 at 15:31
  • @JRL: Thanks. Derived is derived from Base. I have made the change.. – Prabhu Apr 27 '10 at 15:32
  • 2
    Didn't I point out exactly what Yacoby is saying to you a short time ago? If you ignore good advice, you may find people are not going to answer your questions. –  Apr 27 '10 at 15:33

8 Answers8

15

Deleting a derived-class object through a base-class pointer when the base class does not have a virtual destructor leads to undefined behavior.

What you've observed (that the derived-class portion of the object never gets destroyed and therefore its members never get deallocated) is probably the most common of many possible behaviors, and a good example of why it's important to make sure your destructors are virtual when you use polymorphism this way.

Nick Meyer
  • 39,212
  • 14
  • 67
  • 75
8

If the base class does not have a virtual destructor, then the result of your code is undefined behaviour, not necessarily the wrong destructor being called. This is presumably what BoundsChecker is diagnosing.

  • 4
    Spoilt children downvoting technically correct answers is not an edifying spectacle. –  Apr 27 '10 at 15:51
2

Although this is technically undefined, you still need to know the most common method of failure in order to diagnose it. That common method of failure is to call the wrong destructor. I don't know of any implementation that will fail in any other manner, though admittedly I only use two implementations.

The reason this happens is the same reason the 'wrong' function will get called when you try to override a non-virtual member function and call it through a base pointer.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
1

If the destructor is not virtual then the Base destructor will be called. The base destructor cleans up the Base object and finishes. There is no way for the base object destructor to know about the derived object, it must be the derived destructor called, and the way to do that, as with any function, is to make the destructor virtual.

tloach
  • 8,009
  • 1
  • 33
  • 44
  • This isn't so, as I and others have pointed out, what you get is undefined behaviour, which does not necessarily mean the base destructor gets called. –  Apr 27 '10 at 15:40
  • 1
    Calling the base destructor only is the most likely manifestation of the undefined behaviour being invoked here though, which is helpful to know for debugging purposes. – JoeG Apr 27 '10 at 15:45
  • @Neil: Yes, but I'd expect the base destructor to get called most of the time, just like I'd expect `int i = 3; i = i++ + i++;` to leave `i` containing a small integer with no other state changes. It's significant in this case because a plausible behavior leads to the observed results, and strengthens the probability that the lack of a virtual destructor is what's causing the problem. Think of it as more of a diagnostic problem rather than an issue of conformance. – David Thornley Apr 27 '10 at 15:47
  • @Neil - this isn't comp.lang.c++. We don't need to ignore the real world in order to get pedantic over what the standard says. – Edward Strange Apr 27 '10 at 15:48
  • @David I find the idea of conformance more useful - if he had provided a virtual destructor, he wouldn't need to diagnose anything. –  Apr 27 '10 at 15:49
1

From the C++ FAQ Lite: "When should my destructor be virtual?" Read it here. (C++ FAQ Lite is an excellent source for all your questions related to C++, by the way).

Captain Sensible
  • 4,946
  • 4
  • 36
  • 46
1

In C++, a trivial destructor is a recursively defined concept -- it's a destructor that the compiler wrote for you when every member of the class (and every base class) has a trivial destructor. (There's a similar concept called the trivial constructor.)

When an object with a nontrivial destructor is included in an object (like the vector in your example), then the destructor of the outside object (like your Derived) in is no longer trivial. Even though you didn't write destructor, the C++ compiler automatically wrote a destructor that calls the destructors of any members that have destructors.

So, even though you didn't write anything, the caveats of writing a non-virtual destructor still apply.

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
1

if you are coming from c#, then you were right in wondering why vector is not automatically de-allocated. But in c++, automatic memory management is not availble unless you use the Microsoft Manged Extesions to C++ (C++/CLI).

since there is no destructor in Base class that is virtual, the derived class object will never be freed and there-by you leak the memory allocated for the vector data member of the the derived class.

veeru
  • 31
  • 4
0

Destructor is the member function of the class whose name is the same name of the class name and it is preceded by the tilde sign(~). Destructor is used to destroy the object of the class when object goes out of scope or you can say that all clean up of class destruction are to be done in destructor. All the memory gets allocated during construction of the object in class gets destructed (or memory release) when object goes out of scope.

Find more details with example on BoundsCheck