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. …… |