On destructors, interfaces and memory leaks

Over the last month and a half* one of the things I’ve been tasked with was removing memory leaks from our software. At first, it looked like as if I was presented with a daunting responsibility, with the VLD spewing several thousands of apparent memory leaks after just a sample test-run of our main program. Fortunately for me, the vast majority of the leaks seemed to originate from three interface classes.

Interestingly enough, all the leaks seemed to come not from the interfaces themselves, but from derived classes with even standard classes like std::string being leaked. My initial guess was that the owner thread was ungracefully terminated, but that neither happened in the code nor explained the lack of any leaks in the interfaces themselves. I admit that this behaviour baffled me, because I carefully investigated said classes and they seemed perfectly fine – at least from a memory management point of view.

Fortunately, upon closer investigation I noticed that the only called destructors were those of the interface classes. It didn’t take much more to notice that delete is called on pointers to interfaces and deduce that the compiler simply didn’t know to generate a call to the destructor of the implementation class. So, I deduced, that basically I was in a situation much like Leaky classes from the example below:

#include <iostream>
 
using namespace std;
 
class LeakyBase{
public:
        ~LeakyBase(){}
};
 
class PluggedBase{
public:
        virtual ~PluggedBase(){}
};
 
class LeakyDerived : public LeakyBase{
public:
        LeakyDerived() { cout << "LeakyDerived()" << endl; }
        ~LeakyDerived() { cout << "~LeakyDerived()" << endl; }
};
 
class PluggedDerived : public PluggedBase{
public:
        PluggedDerived() { cout << "PublicDerived()" << endl; }
        ~PluggedDerived() { cout << "~PluggedDerived()" << endl; }
};
 
int main()
{
        LeakyBase * l = new LeakyDerived;
        PluggedBase * p = new PluggedDerived;
 
        delete l;
        delete p;
}

Output:

LeakyDerived()
PublicDerived()
~PluggedDerived()

As can be seen in the Plugged classes in the example above, adding as little as one keyword in the interface definition solves this problem completely. By declaring the destructor virtual we can ensure that proper destructor will be chosen at runtime, even if we call it on a pointer to the interface class. After implementing these changes in my interface definitions and recompiling, I managed to remove over 90% of memory leaks.

The fix could be called “simple”, because I added just three words and reduced the total number of memory leaks by thousands. In a way it is, because if I need to remove memory leaks ever again, I’ll be sure to check interface classes for virtual destructors – and it’ll be fast. That being said, it took me some time to find this. So, if any of my readers (can I even use plural?) are in similar situation, remember about checking any classes that might have delete called on their base class pointers for virtual destructors!

* This post was written around the middle of may, but I didn’t get to post it until now, so the time period I’m talking about is from the end of march to mid-may.

3 thoughts on “On destructors, interfaces and memory leaks

    1. Trust me, you can leak memory in Java, and it’s much harder to even acknowledge because of the popular opinion that java has no pointers or its GC doesn’t leak.

      Besides, I wouldn’t want to code in a language that doesn’t even have unsigned integers.

    2. ну и очевидное – экземпляры класса должны быть созданы на куче :) class CBase{ int m_i;public: void MyFunction();};void CBase::MyFunction(){ delete this;}int main (){ CBase a, *b = new CBase(); b->MyFunction(); a.MyFunction();//run time error}

Leave a Reply

Your email address will not be published.