VC++6.0中記憶體洩漏檢測

licup123發表於2009-02-08
 VC++6.0中記憶體洩漏檢測(轉)

VC++6.0中記憶體洩漏檢測
這篇文章是對2004-09-02日發表的《VC++6.0中簡單的記憶體洩漏檢測事例程式碼》(已經刪除)的更新.
對C++程式碼而言,記憶體洩漏問題雖然有諸多方法避免,但實際程式碼編寫的時候,或出於自信或出於複雜性的考慮,常常還會用到原始的operator new,這不可避免的會帶來記憶體洩漏的可能,不久前本人因為違反了"可用於被多型繼承的基類其解構函式應當有virtual修飾"的法則( 一不小心就忘了寫virtual ^_^ ),導致了記憶體洩漏,因此我覺得出於安全考慮,在程式碼中加入記憶體洩漏檢查機制還是很必要的,也因為這次的記憶體洩漏事件促使我寫出這一篇文章.
VC++中本身就有記憶體洩漏檢查的機制,你可以在嚮導生成的支援MFC的工程中看到如下程式碼:
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
通過它們,你能非常容易的發現程式碼中的記憶體洩漏,但是如果手工將這個功能移植到非MFC工程中去是很繁瑣的一件事,另外它還有一個bug,在多執行緒併發呼叫這個DEBUG_NEW時會導致系統級錯誤,因此本人在此重寫了這個功能,將以下的debug_new.h和debug_new.cpp新增到工程中,並在需要檢測的cpp中#include "debug_new.h"和main中一開始處加入REG_DEBUG_NEW巨集即可.
1. debug_new.h 原始碼
/************************************************************************/
/* comment: 此檔案與debug_new.cpp配合使用,用於在除錯期發現記憶體洩漏 */
/* 僅在VC++編譯器中適用(包括Intel C++,因為它使用了相同的庫) */
/* 作者: 周星星
http://blog.vckbase.com/bruceteen/ */
/* 版權申明: 無,可任意 使用,修改 和 釋出 */
/************************************************************************/
/* sample
#include
#include "debug_new.h" // +
using namespace std;
int main( void )
{
REG_DEBUG_NEW; // +
char* p = new char[2];
cout << "--End--" << endl;
return 0;
}
在VC++ IDE中按F5除錯執行將會在Output視窗的Debug頁看到類似如下的提示:
Dumping objects ->
d:\test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
Data: < > CD CD
Object dump complete.
如果不出現如上提示請Rebuild All一次.
*/
#ifndef _DEBUG_NEW_H_
#define _DEBUG_NEW_H_
#ifdef _DEBUG
#undef new
extern void _RegDebugNew( void );
extern void* __cdecl operator new( size_t, const char*, int );
extern void __cdecl operator delete( void*, const char*, int);
#define new new(__FILE__, __LINE__)

#define REG_DEBUG_NEW _RegDebugNew();
#else
#define REG_DEBUG_NEW
#endif // _DEBUG
#endif // _DEBUG_NEW_H_

2. debug_new.cpp 原始碼
/************************************************************************/
/* comment: 此檔案與debug_new.h配合使用,用於在除錯期發現記憶體洩漏 */
/* 僅在VC++編譯器中適用(包括Intel C++,因為它使用了相同的庫) */
/* 作者: 周星星
http://blog.vckbase.com/bruceteen/ */
/* 版權申明: 無,可任意 使用,修改 和 釋出 */
/************************************************************************/
//#include "debug_new.h"
#ifdef _DEBUG
#include
#include
class _CriSec
{
CRITICAL_SECTION criSection;
public:
_CriSec() { InitializeCriticalSection( &criSection ); }
~_CriSec() { DeleteCriticalSection( &criSection ); }
void Enter() { EnterCriticalSection( &criSection ); }
void Leave() { LeaveCriticalSection( &criSection ); }
} _cs;
void _RegDebugNew( void )
{
_CrtSetDbgFlag( _CRTDBG_REPORT_FLAG | _CRTDBG_LEAK_CHECK_DF );
}
void* __cdecl operator new( size_t nSize, const char* lpszFileName, int nLine )
{
// comment 1: MFC中提供的debug new雖然加了鎖,但我在實際測試的時候發現多執行緒併發
// 呼叫的時候還是丟擲了系統錯誤,所以我在這裡加了一個執行緒互斥量.
// comment 2: debug new和debug delete之間需不需要互斥我並不知道,保險起見,我同樣
// 加了執行緒互斥量.
// comment 3: 按照C++標準規定,在operator new失敗後應當呼叫set_new_handler設定的
// 函式,但是MSDN中卻說"標頭檔案new中的set_new_handler是stub的,而應該使
// 用標頭檔案new.h中的_set_new_handler",這簡直是滑天下之大稽.
// 以下是VC++6.0中的set_new_handler定義:
// new_handler __cdecl set_new_handler( new_handler new_p )
// {
// assert( new_p == 0 ); // cannot use stub to register a new handler
// _set_new_handler( 0 );
// return 0;
// }
// 所以我也無計可施,只能捨棄set_new_handler的作用.
_cs.Enter();
void* p = _malloc_dbg( nSize, _NORMAL_BLOCK, lpszFileName, nLine );
_cs.Leave();
return p;
}
void __cdecl operator delete( void* p, const char* /*lpszFileName*/, int /*nLine*/ )
{
_cs.Enter();
_free_dbg( p, _CLIENT_BLOCK );
_cs.Leave();
}
#endif

3. 事例程式碼
#include
#include "debug_new.h"
using namespace std;
int main( void )
{
REG_DEBUG_NEW;
char* p = new char[2];
p[0] = 'A';
p[1] = 'B';
cout << "--End--" << endl;
return 0;
}

4. 結果輸出
在VC++ IDE中按F5除錯執行將會在Output視窗的Debug頁看到類似如下的提示:
……
Dumping objects ->
d:\test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
Data: 41 42
Object dump complete.
……

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10697500/viewspace-545586/,如需轉載,請註明出處,否則將追究法律責任。

相關文章