Tom Lessing
November 13th, 2000, 07:16 AM
Every now and again I hear people growl and moan about C++ and MFC. Recently I made a stupid C++ mistake that made the CString class and template classes miss behave (according to my logic for the moment). Thought I would share it with everyone ..
Ever seen the debugger report something like this when you terminate your program?
----------------------------------------
strcore.cpp(118) : {54} normal block at 0x00301AC0, 19 bytes long.
Data: < Tomc> 01 00 00 00 06 00 00 00 06 00 00 00 54 6F 6D 63
plex.cpp(31) : {53} normal block at 0x00301B00, 124 bytes long.
Data: < 0 > 00 00 00 00 00 00 00 00 10 1B 30 00 0A 00 00 00
----------------------------------------
After a bit of an investigation one can trace the problem to CList and CString in this case very mysteriously not freeing its memory. MSDN provides no insight into the problem either.
Consider the following code snippet ...
/////////////////////////////////////////////////////////////////////////////
//
// Memory leak bug illustrated using CString classes
class CHuman
{
public:
CString m_strName;
CHuman();
~CHuman();
};
// 10/11/2000, TL: Created
CHuman::CHuman()
{
};
// 10/11/2000, TL: Created
CHuman::~CHuman()
{
};
/////////////////////////////////////////////////////////////////////////////
//
class CMan: public CHuman
{
public:
CString m_strNickName;
CList<int,int> m_lstTest;
public:
CMan();
~CMan();
};
/////////////////////////////////////////////////////////////////////////////
//
// 10/11/2000, TL: Created
CMan::CMan()
{
m_lstTest.AddHead(10);
m_lstTest.AddHead(20);
};
/////////////////////////////////////////////////////////////////////////////
//
// 10/11/2000, TL: Created
CMan::~CMan()
{
};
I used these two classes for demonstration purposes. They are simple and illustrate the behaviour nicely. To show the bug I used the following code to instatiate an object of type man called pMe. Added some stuff to it and finally deleted the object again.
//create a man (me that is)
CHuman *pMe = new CMan;
//C++ cast
//reinterpret_cast<CMan *>(pMe) ->m_strNickName = "Tomcat";
//C cast (which ever you prefer)
((CMan *)pMe) ->m_strNickName = "Tomcat";
//tell the me object my name
pMe ->m_strName = "Tom";
//delete the me object
delete pMe;
//Memory leak occurs when terminating APPLICATION !!!!
//Why does the pMe object report that tomcat is leaked but not tom?
Still wondering what the problem is?
Use the debugger and set a break point in the CMan class's destructor. Run the app again and see what happens. A quick trail and error test reveals that the CMan class's destructor never gets called. Why not? Whenever you create classes that are to be inherited ALWAYS declare your destructors to be virtual. Not doing this can easily lead to destructors not doing what it is supposed to be doing. The more classes you have and the more casting you do the better your chances of running into this kind of a problem.
I've heard some novice programmers often complain something like 'MFC is so buggy it leaks memory all over the place' or 'this class or that class doesn't work'. The bottom line is there is nothing wrong with most of the classes being it in STL or in MFC, they all work pretty well in most cases. I think this kind of bug is often blamed at bad coding on the compiler coders behalf. I hope that Microsoft and competitors will document more of this kind of strange 'buggy' behaviour. That way it is easier to solve this kind of bug that could appear to be very strange.
-----------
Assumption is the mother of all ...
Movie: Crimson Tide
Ever seen the debugger report something like this when you terminate your program?
----------------------------------------
strcore.cpp(118) : {54} normal block at 0x00301AC0, 19 bytes long.
Data: < Tomc> 01 00 00 00 06 00 00 00 06 00 00 00 54 6F 6D 63
plex.cpp(31) : {53} normal block at 0x00301B00, 124 bytes long.
Data: < 0 > 00 00 00 00 00 00 00 00 10 1B 30 00 0A 00 00 00
----------------------------------------
After a bit of an investigation one can trace the problem to CList and CString in this case very mysteriously not freeing its memory. MSDN provides no insight into the problem either.
Consider the following code snippet ...
/////////////////////////////////////////////////////////////////////////////
//
// Memory leak bug illustrated using CString classes
class CHuman
{
public:
CString m_strName;
CHuman();
~CHuman();
};
// 10/11/2000, TL: Created
CHuman::CHuman()
{
};
// 10/11/2000, TL: Created
CHuman::~CHuman()
{
};
/////////////////////////////////////////////////////////////////////////////
//
class CMan: public CHuman
{
public:
CString m_strNickName;
CList<int,int> m_lstTest;
public:
CMan();
~CMan();
};
/////////////////////////////////////////////////////////////////////////////
//
// 10/11/2000, TL: Created
CMan::CMan()
{
m_lstTest.AddHead(10);
m_lstTest.AddHead(20);
};
/////////////////////////////////////////////////////////////////////////////
//
// 10/11/2000, TL: Created
CMan::~CMan()
{
};
I used these two classes for demonstration purposes. They are simple and illustrate the behaviour nicely. To show the bug I used the following code to instatiate an object of type man called pMe. Added some stuff to it and finally deleted the object again.
//create a man (me that is)
CHuman *pMe = new CMan;
//C++ cast
//reinterpret_cast<CMan *>(pMe) ->m_strNickName = "Tomcat";
//C cast (which ever you prefer)
((CMan *)pMe) ->m_strNickName = "Tomcat";
//tell the me object my name
pMe ->m_strName = "Tom";
//delete the me object
delete pMe;
//Memory leak occurs when terminating APPLICATION !!!!
//Why does the pMe object report that tomcat is leaked but not tom?
Still wondering what the problem is?
Use the debugger and set a break point in the CMan class's destructor. Run the app again and see what happens. A quick trail and error test reveals that the CMan class's destructor never gets called. Why not? Whenever you create classes that are to be inherited ALWAYS declare your destructors to be virtual. Not doing this can easily lead to destructors not doing what it is supposed to be doing. The more classes you have and the more casting you do the better your chances of running into this kind of a problem.
I've heard some novice programmers often complain something like 'MFC is so buggy it leaks memory all over the place' or 'this class or that class doesn't work'. The bottom line is there is nothing wrong with most of the classes being it in STL or in MFC, they all work pretty well in most cases. I think this kind of bug is often blamed at bad coding on the compiler coders behalf. I hope that Microsoft and competitors will document more of this kind of strange 'buggy' behaviour. That way it is easier to solve this kind of bug that could appear to be very strange.
-----------
Assumption is the mother of all ...
Movie: Crimson Tide