In this article, I will demonstrate some typical scenarios that cause memory
leak. By the end of each scenario, I will also put analysis and fix. If you are interested, you can take a quick test to see whether you can figure out the error immediately.
Note: Next samples are tested with visual c++ 6.0, Different compilers or
different versions may have different behaviors.
1. X is not Y
1: class A{ };
2: class B : public A {
3: private:
4: std::string str;
5: public: B() {
6: str="Hello";
7: }
8: };
9: void LeakTest() {
10: A * pA = new B();
11: if( pA )
12: delete pA;
13: }
Comments:
(1)The destructor of B is not defined, so the compiler will automatically
generate one, in which the destructor of std::string will be called for str
defined in line 4, and the internal buffer that holds "Hello" will be freed.
(2)Compiler also generates a default destructor for class A, but it
is not virtual.
ERROR: pA is deleted in line 12, Because pA is a pointer of class A
and the default destructor of class A is not virtual, only the destructor of
class A will be called, thus the internal buffer allocated by str will be leaked.
fix: add virtual destructor for class A, virtual ~A(){}
2. X and Y doesn't Z
1: class A{
2: private:
3: std::string str;
4: pubilc:
5: A(){ str = "Hello";}
6: };
7: void LeakTest1(){
8: A* pArray = new A[100];
9: if( pArray )
10: delete pArray;
11: }
comments:
With debug version of this sample, you will see a assert failure window
because the pointer passed to CRT delete function are different from pArray, CRT
function detected it and report the error.
With release version, the sample can ran, but it leaks memory. line 10 only
delete the internal buffer of the first item of the array.
fix: change line 10 to: delete[] pArray;
3. Store pointers in container
class A{
int a;
};
class MyContainer{
public:
std::map<long,A*> leakMap;
~MyContainer(){
std::map<long,A*>::iterator it;
for(it=leakMap.begin();it!=leakMap.end();it++){
if( *it != NULL){
delete
*it;
}
}
};
void LeakTest() {
MyContainer con;
A* pA1 = new A();
A* pA2 = new A();
con.leakMap[1000] = pA1;
con.leakMap[1000] = pA2;
}
Sometimes it maybe necessory to store pointers in container. When you
replace existing pointer in container with new pointer. make sure delete the old pointer.
error: when the sample put pA2 into the container,
4. Soemtimes reference counting makes the investigation difficult.
void LeakTest( std::string& str ){
std::string temp("Hello");
str=temp;
}
void Caller() {
std::string* pStr = new std::string();
LeakTest( pStr );
}
error: There are two leaks here, one is the "new std::string()", the other is the
"std::sting temp("Hello")";the seconds is not released in its destructor because it referenced by another
std::string, which is the input parameter str. So when the tool comlains about the leak, and it looks like it should be freed in the destructor. You can think about reference couting.
5. COM
_bstr_t GetInfo( ){
BSTR A = SysAllocString("Hello");
_bstr_t B = A;
return B;
}
error: _bstr_t B in this sample will create a copy of BST A, B did not
attach to A, so A should be free by calling SysFreeString before leaving the
function.
6 void *
void ThreadFunc( void * p ) {
// Do business logic
if( p ) delete p; // let us assume it must be deleted here
} void LeakTest(){ A* p = new A(); _beginthread( ThreadFunc,(void*)p ); }
error:
This one is straight forward. In function ThreadFunc(), p should be converted to real type A to be deleted.
Conclusion:
The error code may seems simple, but in real world especially in a big application and the code has a lot of business logic. The error showed in this article maybe not so obviously. More attention paid to such lessons, more problems could be prevented.