Question

Why does this code cause memory leak? It's said that if the base class destructor is made "virtual", the memory leak problem would be solved. Why?

class base {
 public: 
   base () { ptr = new int[1024]; }
   ~base () { delete [] ptr; }
 private:
   double *ptr;
};
class der : public base {
 public:
   der () { name = new char[1024]; }
   ~der () { delete [] name; }
 private:
   char *name;
};
int main () {
  base* aPointer = new derived;
  delete aPointer;
  return 0;
}
Was it helpful?

Solution 2

It leaks, and it's Undefined Behaviour.

If your derived class didn't introduce extra heap-allocated variables, then it probably wouldn't but would still be Undefined Behaviour so speculation's pointless.

Unless base has a virtual destructor, deletion of derived objects using a base* will fail to call destructors for the further derived classes and their members and other bases... here name would not be deallocated.

It's said that if the base class destructor is made "virtual", the memory leak problem would be solved. Why?

virtual functions are the way the compiler coordinates calls to derived-class-specific functions from code that only has a pointer or reference to the base class. This is a fundamental aspect of Object Oriented Programming support in C++, and if you don't understand it you should get a good book or online tutorial, but to give a very concise introduction: a virtual function works a bit like having a function pointer in the base class that normally points to the base class's function implementation, but if a derived class constructor runs then the function pointer is overwritten with the address of its own implementation (in practice virtual dispatch tends to use class-specific tables of function pointers for better memory efficiency, but the observable functionality is similar to what I've described). So, your derived destructor won't run when you delete using a pointer with static type of base* unless it's virtual - there's no function-pointer-like indirection coordinated by the derived type that could arrange this.

OTHER TIPS

Both classes Base and Derived do not have virtual destructors, which means the destructor is not in vtable, so when you write:

Base * base = new Derived();
...
delete base;

Typically, what happens is that only Base::~Base() will be called, which means Derived::~Derived() is not called, which in turn means that you will get memory leaks if you are expecting Derived::~Derived() to be called.

The solution to this problem is to make the destructor virtual so that it gets put into the vtable. Thus "delete base;" will call the destructor of the dynamic type (Derived) and destroy the object correctly.

When you delete an object, there are two ways C++ might decide what destructor function to call.

If the pointer to that object has been declared to be of type T*, but T::~T() is a virtual function, then C++ looks in a virtual function table to find which destructor to call. The virtual function table is found via a pointer stored within the memory of that instance of the object, which is determined by the class that was actually constructed, not by the type of the pointer. For the object initialized by base* aPointer = new der, if ~base() had been declared virtual, C++ would be able to look in the virtual function table of the actual concrete class (der, not base), and when it did so, it would find the destructor function ~der(). This would delete name and then would call the destructor defined by the class der was derived from; that is, it would call ~base(), which deletes ptr. If base had been derived from some other class, its destructor would call that class's destructor, and so on until reaching a class that was not derived from anything.

But if the pointer to the object has been declared to be of type T*, and the destructor of T is not virtual, then the only destructor the compiler can use is the one that was defined when you defined T, namely, T::~T(). So, because aPointer was declared to be a base*, delete aPointer will first call ~base(), which deletes ptr, and then it returns, because there are no further base classes whose destructors can be called. The destructor ~der() is never called in this case, so name is not deleted. That's a memory leak.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top