有了這樣的定義,在編譯DEBUG版時,出如今這個cpp文件中的全部new都被替換成DEBUG_NEW了。那麼DEBUG_NEW是什麼呢?DEBUG_NEW也是一個宏,如下摘自afx.h,1632行
#define DEBUG_NEW new(THIS_FILE, __LINE__)
因此若是有這樣一行代碼:
char* p = new char[200];
通過宏替換就變成了:
char* p = new( THIS_FILE, __LINE__)char[200];
根據C++的標準,對於以上的new的使用方法,編譯器會去找這樣定義的operator new:
void* operator new(size_t, LPCSTR, int)
咱們在afxmem.cpp 63行找到了一個這樣的operator new 的實現
void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine)
{
return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
}
void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)
{
…
pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);
if (pResult != NULL)
return pResult;
…
}
第二個operator new函數比較長,爲了簡單期間,我只摘錄了部分。很顯然最後的內存分配仍是經過_malloc_dbg函數實現的,這個函數屬於MS C-Runtime Library 的Debug Function。這個函數不但要求傳入內存的大小,另外還有文件名和行號兩個參數。文件名和行號就是用來記錄這次分配是由哪一段代碼形成的。若是這塊內存在程序結束以前沒有被釋放,那麼這些信息就會輸出到Debug窗口裏。
這裏順便提一下THIS_FILE,__FILE和__LINE__。__FILE__和__LINE__都是編譯器定義的宏。當碰到__FILE__時,編譯器會把__FILE__替換成一個字符串,這個字符串就是當前在編譯的文件的路徑名。當碰到__LINE__時,編譯器會把__LINE__替換成一個數字,這個數字就是當前這行代碼的行號。在DEBUG_NEW的定義中沒有直接使用__FILE__,而是用了THIS_FILE,其目的是爲了減少目標文件的大小。假設在某個cpp文件中有100處使用了new,若是直接使用__FILE__,那編譯器會產生100個常量字符串,這100個字符串都是這個cpp文件的路徑名,顯然十分冗餘。若是使用THIS_FILE,編譯器只會產生一個常量字符串,那100處new的調用使用的都是指向常量字符串的指針。
再次觀察一下由MFC Application Wizard生成的項目,咱們會發如今cpp文件中只對new作了映射,若是你在程序中直接使用malloc函數分配內存,調用malloc的文件名和行號是不會被記錄下來的。若是這塊內存發生了泄漏,MS C-Runtime Library仍然能檢測到,可是當輸出這塊內存塊的信息,不會包含分配它的的文件名和行號。
要在非MFC程序中打開內存泄漏的檢測功能很是容易,你只要在程序的入口處加入如下幾行代碼:
int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag( tmpFlag );
這樣,在程序結束的時候,也就是winmain,main或dllmain函數返回以後,若是還有內存塊沒有釋放,它們的信息會被打印到Debug窗口裏。
若是你試着建立了一個非MFC應用程序,並且在程序的入口處加入了以上代碼,而且故意在程序中不釋放某些內存塊,你會在Debug窗口裏看到如下的信息:
{47} normal block at 0x00C91C90, 200 bytes long.
Data: < > 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
內存泄漏的確檢測到了,可是和上面MFC程序的例子相比,缺乏了文件名和行號。對於一個比較大的程序,沒有這些信息,解決問題將變得十分困難。
爲了可以知道泄漏的內存塊是在哪裏分配的,你須要實現相似MFC的映射功能,把new,maolloc等函數映射到_malloc_dbg函數上。這裏我再也不贅述,你能夠參考MFC的源代碼。
因爲Debug Function實如今MS C-RuntimeLibrary中,因此它只能檢測到堆內存的泄漏,並且只限於malloc,realloc或strdup等分配的內存,而那些系統資源,好比HANDLE,GDI Object,或是不經過C-Runtime Library分配的內存,好比VARIANT,BSTR的泄漏,它是沒法檢測到的,這是這種檢測法的一個重大的侷限性。另外,爲了能記錄內存塊是在哪裏分配的,源代碼必須相應的配合,這在調試一些老的程序很是麻煩,畢竟修改源代碼不是一件省心的事,這是這種檢測法的另外一個侷限性。