您的位置:首页 > 编程语言 > C语言/C++

C++下面的内存泄露检测

2010-03-06 21:10 267 查看
内存的管理机制是写代码过程中一个比较重要的功能模块,因为内存泄露是很难调试出来的,会给软件埋下定时炸弹。在C++

环境下有相关的内存检测的方式,这里简单的罗列一下,避免以后自己会忘记,基本上是参考网络上面一篇文章:

在VC下面有这样的一个头文件crtdbg.h,专门用于内存的管理。

我们会用到里面很重要的几个函数。其中最重要的是 _CrtDumpMemoryLeaks();自己看MSDN里的帮助吧。使用这个函数,需要包含头文件crtdbg.h

该函数只在Debug版本才有用,当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在“Output(输出)”窗口中显示内存泄漏信息.写段代码试验一下吧,如下:

#include "stdafx.h"

#include <crtdbg.h>

int _tmain(int argc, _TCHAR* argv[])

{

int* p = new int();

_CrtDumpMemoryLeaks();

return 0;

}

注意这个函数只有debug条件下才会有作用,并且答应的信息输出在vc下面的output窗口中。

Detected memory leaks!

Dumping objects ->

{104} normal block at 0x00697870, 4 bytes long.

Data: < > 00 00 00 00

Object dump complete.

The program '[4688] MemDebug.exe: Native' has exited with code 0 (0x0).

但是这个函数还是很郁闷,我们不知道内存泄露在什么地方,于是我们有了内存泄露的第二个版本。

在引入这个头文件之前,假如这样的宏

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

#include <crtdbg.h>

int _tmain(int argc, _TCHAR* argv[])

{

int* p = new int();

_CrtDumpMemoryLeaks();

return 0;

}

我们会发现现在的打印信息为

Detected memory leaks!

Dumping objects ->

i:/vc_test/memdebug/memdebug/memdebug.cpp(21) : {104} client block at 0x003B7870, subtype 0, 4 bytes long.

Data: < > 00 00 00 00

Object dump complete.

The program '[3064] MemDebug.exe: Native' has exited with code 0 (0x0).

这样就包含了泄露内存最开始分配的时候的地址。

该程序定义了几个宏,通过宏将Debug版本下的new给替换了,新的new记录下了调用new时的文件名和代码行.

但是这个还不是最终版本,我们可以这样的代码来测试:

int _tmain(int argc, _TCHAR* argv[])

{

int* p = new int();

_CrtDumpMemoryLeaks();

delete p;

return 0;

}

发现我们释放掉的时候,还是有提示说内存错误,这个就有问题了。

运行后可以发现我们删除了指针,但是它仍然报内存泄露。所以可以想象,每调用一次new,程序内部都会将该调用记录

下来,类似于有个数组记录,假如delete了,那么就将其从数组中删除,而_CrtDumpMemoryLeaks()就是把这个数组

当前的状态打印出来。所以除了在必要的时候Dump出内存信息外,最重要的就是在程序退出的时候需要掉用一次_CrtDumpMemoryLeaks();

假如程序有不止一个出口,那么我们就需要在多个地方都调用该函数。

这个就是说,我们把debug模式下的new改变了一下,这个时候每一次new都会忘某个调试分配的表中添加一条信息,如果

delete,这个信息就会删掉,我们只能在程序最终退出的地方加上这样的一个函数CrtDumpMemoryLeaks表示需要打印

内存泄露。程序改成这样就会没有:

int _tmain(int argc, _TCHAR* argv[])

{

int* p = new int();

delete p;

_CrtDumpMemoryLeaks();

return 0;

}

根据这个原理我们可以推断出下面的程序也会出现内存泄露的错误:

class Test

{

public:

Test() { _p = new int(); }

~Test() { delete _p; }

int* _p;

};

int _tmain(int argc, _TCHAR* argv[])

{

int* p = new int();

delete p;

Test t;

_CrtDumpMemoryLeaks();

return 0;

}

造成这个的原因就是我们没有保证这个函数是在程序最后调用的,因为Test t是一个局部类,在程序退出的时候,程序会自动

的调用析构,这个调用析构在CrtDumpMemoryLeaks之前,所以就会有这样的问题,如何来改进呢?如何保证我们的这个CrtDumpMemoryLeaks的调用一定在程序的最后,这个在我们自己的代码中是无法做到的,因为C++的析构是编译器的

特性,析构的调用是编译器自己调用的。

但是我们有这样的一个宏,让编译器知道程序退出的时候调用我们这个函数:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

代码如下:

class Test

{

public:

Test() { _p = new int(); }

~Test() { delete _p; }

int* _p;

};

int _tmain(int argc, _TCHAR* argv[])

{

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

int* p = new int();

delete p;

Test t;

return 0;

}

但是我觉得光这样还不够,因为我们只是在Output窗口中输出信息,对开发人员的提醒还不明显,经常会被遗漏,而且很

多人就算发现了内存泄露,但是不好
修复,不会严重影响到程序外在表现,都不会修复。怎么样能让开发人员主动的修

复内存泄露的问题呢?记得曾经和人配合写程序,我的函数参数有要求,不能为
空,但是别人老是传空值,没办法了,

只好在函数开始验证函数参数,给他assert住,这样程序运行时老是不停的弹出assert,调试程序那个烦压,最
后其他程

序员烦了,就把这个问题给改好了,输入参数就正确了。所以我觉得咱要让程序员主动去做一件事,首先要让他觉得做这个

事是能减轻自己负担,让自己工
作轻松的。呵呵,那咱们也这样,当程序退出时,检测到内存泄露就让程序提示出来。

代码如下:

#include "stdafx.h"

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

#include <crtdbg.h>

#include <assert.h>

class Test

{

public:

Test() { _p = new int(); }

~Test() { }

int* _p;

};

void Exit()

{

int i = _CrtDumpMemoryLeaks();

assert( i == 0);

}

int _tmain(int argc, _TCHAR* argv[])

{

atexit(Exit);

int* p = new int();

delete p;

Test t;

return 0;

}

运行起来果然发现,Test析构的时候没有释放掉内存,现在终于知道xp下面经常弹出来的那个终止忽略调试对话框是如何

出来的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: