Saturday, 14 May 2011

Virtual Destructor and Memory Leak

I met a memory leak problem when I debug a small application. When application exist, the memory leak detector always report that there are some memory leaks. There just some information about block number. It looks like following:

Detected memory leaks!
Dumping objects ->
{60} normal block at 0x00324818, 4 bytes long.
Data: <, > 2C 00 00 00
Object dump complete.

There is not cpp file name and line number. So I can’t find the place directly. I add a memory allocation detection function to locate this memory leak. This method is just valid when the memory leak always appears in same place no matter application runs how much times.

_CrtSetBreakAlloc(60)

After that, I located memory leak here:

CString szRet;
   GetPrivateProfileString (TEXT("Section1"),
                            TEXT("FirstKey"),
                            TEXT("Error: GPPS failed"),
                            szRet,
                            80,
                            TEXT("appname.ini"));
m_szKey = szRet;

It means the m_szKey is allocated but never released. I checked the class definition file. m_szKey is declared as a normal variable.

CString m_szKey;

It is strange. Because m_szKey is just a normal variable, it will be destroyed automatically when object is released, event the destructor didn't destroy it explicitly. Could it been said that this object’s destructor is not been called? Then I checked definition of this class. It looks like following:

Class A
{
       public:
              A();
              ~A();
}

Class B : public A
{
       public:
              B();
~B();
       private:
              CString m_szKey;
}

In application, B was used like this:

CList<A*, A*> lstAB;
B* b = new B();
lstAB.add(b);

Right now, you should find out the reason for memory leak. Parent class A didn’t declare its destructor as virtual. In application, B’s object was added in a list which is declared as A*. This is a normal usage of Polymorphism. When we called some object from this list, this object may behave like A or like B. It depends on its real type. But when this list was destroyed, all objects inside this list just treated as A. Just ~A() was called and ~B() never been called. m_szKey is B’s private member and never been destroyed.

I changed this code and declared ~A() as virtual. I run application again. Ok, memory leak disappeared.