C++中运用Crt的内存调试功能检测内存泄露

#ifdef_DEBUG#defineDEBUG_CLIENTBLOCKnew(_CLIENT
【菜科解读】
尽管这个概念已经让人说滥了 ,还是想简单记录一下, 以备以后查询。
#ifdef_DEBUG#defineDEBUG_CLIENTBLOCKnew(_CLIENT_BLOCK,__FILE__,__LINE__)#else#defineDEBUG_CLIENTBLOCK#endif#define_CRTDBG_MAP_ALLOC#include#ifdef_DEBUG#definenewDEBUG_CLIENTBLOCK#endifint_tmain(intargc,_TCHAR*argv[]){char*p=newchar();char*pp=newchar[10];char*ppp=(char*)malloc(10);_CrtDumpMemoryLeaks();return0;}
主要原理是运用Crt 的内存调试功能,通过宏替代默认的operator new, 让它被下面版本替代:
void*__CRTDECLoperatornew(size_tcb,intnBlockUse,constchar*szFileName,intnLine)_THROW1(_STDbad_alloc){/*_nh_malloc_dbgalreadycalls_heap_alloc_dbginaloopandcalls_callnewhiftheallocationfails.If_callnewhreturns(verylikelybecausenonewhandlershavebeeninstalledbytheuser),_nh_malloc_dbgreturnsNULL.*/void*res=_nh_malloc_dbg(cb,1,nBlockUse,szFileName,nLine);RTCCALLBACK(_RTC_Allocate_hook,(res,cb,0));/*iftheallocationfails,wethrowstd::bad_alloc*/if(res==0){staticconststd::bad_allocnomem;_RAISE(nomem);}returnres;}
这样Crt会把此次分配内存的文件名和行号以及大小等记录下来,最后当调用用_CrtDumpMemoryLeaks();时如果还没释放就会打印出来。
结果如下:
Detectedmemoryleaks!Dumpingobjects->f:\test\memleakchecker\memleakchecker\memleakchecker.cpp(23):{108}normalblockat0x0003A1A8,10byteslong.Data:CDCDCDCDCDCDCDCDCDCDf:\test\memleakchecker\memleakchecker\memleakchecker.cpp(22):{107}clientblockat0x0003A160,subtype0,10byteslong.Data:CDCDCDCDCDCDCDCDCDCDf:\test\memleakchecker\memleakchecker\memleakchecker.cpp(21):{106}clientblockat0x0003A120,subtype0,1byteslong.Data:00Objectdumpcomplete.
下面是一些注意事项:(1)#define_CRTDBG_MAP_ALLOC 的作用如果不定义这个宏, C方式的malloc泄露不会被记录下来。
(2)数字{108} {107}的作用表示第几次分配, 你可以通过_CrtSetBreakAlloc程序运行到预定次数时暂停,比如
int_tmain(intargc,_TCHAR*argv[]){_CrtSetBreakAlloc(108);char*p=newchar();char*pp=newchar[10];char*ppp=(char*)malloc(10);_CrtDumpMemoryLeaks();return0;}
(3)如果程序有多个出口或是有涉及到全局变量, 可以通过_CrtSetDbgFlag设置标志让程序退出时自动打印泄露 , 比如int_tmain(intargc,_TCHAR*argv[]){_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);char*p=newchar();char*pp=newchar[10];char*ppp=(char*)malloc(10);return0;}
(4)我们知道宏替代是最粗暴的方式, 所以尽量把下面new的替代宏放到每个Cpp里而不是放到一个通用的头文件中, 实际上MFC也是这么做的#ifdef_DEBUG#definenewDEBUG_CLIENTBLOCK#endif
(5)上面的operator new只能照顾到最普通的new, 实际上operator new是有任意多种重载方式, 只需要确保第一个参数是表示大小。
比如下面的placement new就会编译失败, 因为宏替代后格式不符合要求了, 所以如果你的CPP用了非标准的new, 就不要加入new的检测宏了。
#include#ifdef_DEBUG#defineDEBUG_CLIENTBLOCKnew(_CLIENT_BLOCK,__FILE__,__LINE__)#else#defineDEBUG_CLIENTBLOCK#endif#define_CRTDBG_MAP_ALLOC#include#ifdef_DEBUG#definenewDEBUG_CLIENTBLOCK#endifint_tmain(intargc,_TCHAR*argv[]){_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);char*p=newchar();char*pp=newchar[10];char*ppp=(char*)malloc(10);chard;char*p1=new(&d)char('a');return0;}
(6)因为STL里map内的tree用到了placement new, 所以如果你这样用会编译失败:#ifdef_DEBUG#defineDEBUG_CLIENTBLOCKnew(_CLIENT_BLOCK,__FILE__,__LINE__)#else#defineDEBUG_CLIENTBLOCK#endif#define_CRTDBG_MAP_ALLOC#include#ifdef_DEBUG#definenewDEBUG_CLIENTBLOCK#endif#include
你应该把#include放到 宏定义的前面。
(7) 如果你在宏#definenewDEBUG_CLIENTBLOCK 之后再声明或定义 operator new函数, 都会因为宏替代而编译失败。
而STL的xdebug文件恰恰申明了operator new函数, 所以请确保new的替代宏放在所有include头文件的最后, 尤其要放在STL头文件的后面。
//MyClass.cpp
#include"myclass.h"#include#include#ifdef_DEBUG#definenewDEBUG_CLIENTBLOCK#endifMyClass::MyClass(){ char* p = new char('a');}
(8)如果你觉得上面的这种new替代宏分散在各个CPP里太麻烦, 想把所有的东西放到一个通用头文件里,请参考下面定义的方式://MemLeakChecker.h#include#include//otherSTLfile#ifdef_DEBUG#defineDEBUG_CLIENTBLOCKnew(_CLIENT_BLOCK,__FILE__,__LINE__)#else#defineDEBUG_CLIENTBLOCK#endif#define_CRTDBG_MAP_ALLOC#include#ifdef_DEBUG#definenewDEBUG_CLIENTBLOCK#endif
(9)简单判断某个独立函数有没有内存泄露可以用下面的方法:classDbgMemLeak{_CrtMemStatem_checkpoint;public:explicitDbgMemLeak(){_CrtMemCheckpoint(&m_checkpoint);};~DbgMemLeak(){_CrtMemStatecheckpoint;_CrtMemCheckpoint(&checkpoint);_CrtMemStatediff;_CrtMemDifference(&diff,&m_checkpoint,&checkpoint);_CrtMemDumpStatistics( _CrtMemDumpAllObjectsSince( };};int_tmain(intargc,_TCHAR*argv[]){DbgMemLeakcheck;{char*p=newchar();char*pp=newchar[10];char*ppp=(char*)malloc(10);}return0;}
C++,中,运用,Crt,的,内存,调试,功能,检测,泄露,