views:

428

answers:

8

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?

+1  A: 

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
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.
anon
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.
Joe Gauterin
@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
@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.
Noah Roberts
@David I find the idea of conformance more useful - if he had provided a virtual destructor, he wouldn't need to diagnose anything.
anon
+12  A: 

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
+6  A: 

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.

anon
Spoilt children downvoting technically correct answers is not an edifying spectacle.
anon
+1  A: 

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.

Noah Roberts
+1  A: 

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).

Seventh Element
The C++ FAQ book is even better.
Ken Bloom
+1  A: 

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
A: 

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

Boundscheck