几个内存泄漏的例子
new 和 delete 要成对使用
new 和 delete 要匹配
经常看到一些 C++方面的书籍中这样提及到内存泄漏问题,这样的说法的意思是比较明
白,但对于初学 C++程序员还是很难掌握,所以下面举几个反面的例子,希望对大家有帮助。
例一:错误处理流程中的 return 导致的内存泄漏
bool MyFun()
{
CMyObject* pObj = NULL;
pObj = new CMyObject();
…
if (…)
return false;
…
if(…)
return false;
…
if (pObj != NULL)
delete pObj;
return true;
}
注意:红色字体部分的 return 之前没有释放 pObj,导致内存泄漏。
例二:exception 改变了程序的正常流程,导致内存泄漏
情况 1:
HRESULT MyFun()
{
HRESULT hr = S_OK;
try
{
CMyObject* pObj = NULL;
pObj = new CMyObject();
…
if (…)
hr = E_FAIL;
throw hr;
{
}
…
if(…)
{
hr = E_FAIL;
throw hr;
}
…
if (pObj != NULL)
delete pObj;
}
catch (HRESULT& eHr)
{
}
return hr;
}
情况 2:
void OtherFun()
// 可能是自己写的其他函数;
// 也可能是其他人写的函数;
// 也可能是系统的 API;
{
}
…
if(…)
throw exception;
…
bool MyFun()
{
}
CMyObject* pObj = NULL;
pObj = new CMyObject();
…
OtherFun();
…
if (pObj != NULL)
delete pObj;
return true;
注意:上面的两种情况中的 throw 行为将导致程序的正常流程,一旦有 throw 的
动作发生,pObj 对象将不会被正确释放(delete)。
例三:忘记释放系统 API 创建的资源,导致内存泄露
bool CMyClass::MyFun()
{
HANDLE hHandle = CreateEvent(NULL,FALSE,TRUE,NULL);
…
if (…)
return false;
…
return true;
}
注意:系统 API CreateEvent 创建的 HANDLE 对象,需要自己释放,否则会导致内
存泄漏。还有其他一些系统的 API 也是如此,如:CreateFile、CreateFileMapping
等等,所以我们在使用不熟悉的系统 API 时,一定要仔细阅读 MSDN。
例四:PostMessage 可能导致的内存泄漏
// 自定义的消息结构体
typedef struct tagMSG
{
int i;
float f;
}MSG;
// 发送消息的函数
void MyFun()
{
}
MSG* pMsg = new MSG;
…
PostMessage(m_hWnd, WM_MYEVENT, (WPARAM)pMsg, 0);
// 接收消息的函数
afx_msg void OnMessage(WPARAM wParam, LPARAM lParam)
{
}
MSG* pMsg = (MSG*)wParam;
m_i = pMsg->i;
m_f = pMsg->f;
注意:OnMessage 函数中忘记释放 pMsg,导致内存泄漏。
例五:函数返回 new 的对象而导致的内存泄漏
char* MyFun()
{
}
char* p = new char[10];
…
return p;
注意:调用 MyFun 程序的人可能不会注意到 MyFun 函数内部 new 出的对象,往往
会忽略对象 p 的释放。
例六:不好的类结构也会导致内存泄漏
// MyClass.h 文件
class MyClass
{
public:
MyClass();
virtual ~MyClass();
BOOL Init();
BOOL UnInit();
private:
char* m_pStr;
}
// MyClass.cpp 文件
MyClass::MyClass()
: m_pStr(NULL)
{
}
MyClass::~MyClass()
{
}
BOOL MyClass::Init()
{
}
m_pStr = new char[100];
…
if (…)
{
}
delete m_pStr;
m_pStr = NULL;
return FALSE;
return TRUE;
BOOL MyClass::UnInit()
{
}
if (m_pStr != NULL)
{
}
delete m_pStr;
m_pStr = NULL;
return TRUE;
注意:这个类在 Init()函数中 new 出类资源,需要调用相应的 UnInit()函数来释
放资源,但有些时候这个类需要给其他人使用,别人在使用时可能会忘记调用
UnInit()函数来释放资源,这样就造成了内存泄漏,为了防止这个问题发生,最
好在 MyClass::~MyClass()函数中也进行资源释放。如下写法:
MyClass::~MyClass()
{
}
UnInit();
例七:容易忽视的深层次结构对象的内存泄漏
typedef struct MyStruct
{
}
int i;
BSTR bstr;
void MyFun(OLECHAR * sz)
{
}
MyStruct* pStru = new MyStruct;
…
pStru->bstr = SysAllocString(sz);
…
delete pStru;
注意:pStru 是个深层次结构的对象,在这个对象内部还有指针数据类型 bstr,
在释放 pStr 时,如果忘记了释放其内部的指针对象 bstr,也会导致内存泄漏。当
然这个例子比较简单,在我们实际编程时,深层次结构的对象要比这个复杂的多,
所以处理这些对象时需要格外小心。
例八:虚基类的析构函数可能导致的问题
///////////////////////////
// Base.h
class Base
{
public:
Base();
~Base();
virtual void TestFun();
}
// Base.cpp
Base::Base()
{
}
Base::~Base()
{
}
if (m_pStr != NULL)
{
}
delete m_pStr;
void Base::TestFun()
{
}
//////////////////////////////
// MyClass.h
class MyClass : public Base
{
public:
MyClass();
~MyClass();
virtual void TestFun();
protected:
char* m_pStr;
}
// MyClass.cpp
MyClass::MyClass()
: m_pStr(NULL)
{
}
MyClass::~MyClass()
{
}
if (m_pStr != NULL)
delete m_pStr;
void MyClass::TestFun()
{
}
m_pStr = new char[100];
/////////////////////////////
// Test.cpp
int _tmain(int argc, _TCHAR* argv[])
{
}
Base* pClass = new CMyClass();
pClass->TestFun();
delete pClass;
return 0;
注意:由于 Base 类的析构函数不是 virtual 类型,在_tmain 程序的用法中,将会
导致 CMyClass 的析构不会被调用,引起内存泄漏。
总结:
1、 C++编程中,内存泄漏是个比较烦人的问题,创建(new)对象的“入口”一般
只有一处,但需要释放(delete)对象的“出口”却很多,而且由于创建(new)
出来的对象的生存周期不固定,所以要完全避免内存泄漏是比较困难,需要我
们在编程时格外小心;
2、 养成良好的代码书写习惯,尽量在编写代码时解决内存泄漏问题;
3、 一旦程序中发生内存泄漏时,可以使用一些内存泄漏检查工具(如:Bound Check
等)来查找,这些工具可以帮助我们找出部分的内存泄漏的代码;
4、 有些内存泄漏的问题还需要我们手工查找,检查代码的逻辑也是查找内存泄漏
的一种有效手段。