C++项目总结一之内存泄漏检测

来源:互联网 发布:linux 文件不保存退出 编辑:程序博客网 时间:2024/05/22 20:02

在编写C++程序时避免不了要通过malloc/new/realloc来分配堆中的内存;堆中的内存使用完成后由程序员进行释放归还给操作系统。如果动态动态分配的内存没有释放,就会造成内存泄漏。vc提供的运行时函数_CrtDumpMemoryLeaks用于检测在堆中分配的内存没有释放时转储所有泄漏的内存块信息。
程序1:内存泄漏展示

#include <windows.h>#include <stdio.h>#include <crtdbg.h>int main(){    int *p = (int*)malloc(sizeof(int));    _CrtDumpMemoryLeaks();    return 0;}

在vs2008的输出窗口,我们看到内存泄漏提示
这里写图片描述

从图中可以看到共有4字节的内存泄漏。但是从上面中只能看到有4字节的泄漏,如果能够有更多的信息比较提示内存泄漏发生在哪个文件,哪一行就更容易定位问题了。vs2008自带的有一个crtdbg.h的文件,里面有对malloc的重定义;但对malloc的重定义是根据是否定义了_CRTDBG_MAP_ALLOC宏,如果定义的该宏就会重定义malloc,重定义后的malloc会记录分配时的文件名及所在行。

程序2:显示内存泄漏所在行

#include <windows.h>#include <stdio.h>#define _CRTDBG_MAP_ALLOC#include <crtdbg.h>int main(){    int *p = (int*)malloc(sizeof(int));    _CrtDumpMemoryLeaks();    return 0;}

输出窗口信息:
这里写图片描述
到此可以看到在source.cpp的第8行出现了内存泄漏,泄漏4字节。
在上面的程序2中,如果free(p)在_CrtDumpMemoryLeaks()之后,程序运行后仍然会提示内存泄漏。如果定义了全局对象,全局对象是在main运行之后才进行析构函数调用。这样在main函数中调用_CrtDumpMemoryLeaks()还是会检测到内存泄漏,下面看程序3。
程序3:

#include <windows.h>#include <stdio.h>#define _CRTDBG_MAP_ALLOC#include <crtdbg.h>class A{public:    A(){p = (int*)malloc(sizeof(int));}    ~A(){delete p;}private:    int *p;};A obj;int main(){    _CrtDumpMemoryLeaks();    return 0;}

运行上面的程序后,vs2008输出窗口如下:
这里写图片描述
可以看到输出窗口提示程序第8行发生了内存泄漏,实际情况是并没有发生内存泄漏。下面来看另外一个函数_CrtSetDbgFlag,使用这函数通过设置_CRTDBG_ALLOC_MEM_DF及_CRTDBG_LEAK_CHECK_DF,可以在程序退出的时候转储泄漏的内存块。
程序4:

#include <windows.h>#include <stdio.h>#define _CRTDBG_MAP_ALLOC#include <crtdbg.h>class A{public:    A(){p = (int*)malloc(sizeof(int));}    ~A(){delete p;}private:    int *p;};A obj;int main(){    _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);    int *p = (int*)malloc(sizeof(int));    return 0;}

程序运行后输出如下信息:
这里写图片描述
输出窗口显示我们的程序第17行存在没释放的堆内存,而全局对象中则不存在没释放的内存。这个提示的内存泄漏信息就是正确的。而不会误报全局对象的中的内存没有释放。
上面对malloc分配的内存是否泄漏进行了检测,但是对于C++的new进行分配的内存还没有检测。MSDN中有一段对于new运算符进行宏替换的代码:

#ifdef _DEBUG   #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)#else   #define DEBUG_CLIENTBLOCK#endif#ifdef _DEBUG#define new DEBUG_CLIENTBLOCK#endif

程序5:

#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#endifclass A{public:    A(){p = (int*)malloc(sizeof(int));}    ~A(){delete p;}private:    int *p;};A obj;int main(){    _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);    int *p = new int;    return 0;}

程序运行后vs2008窗口输出信息:
这里写图片描述
到此我们程序中,即可以检测出malloc也可以检测出new分配的没释放的内存。
在实际项目中我们发现,上面用于检测内存泄漏所定义的宏几乎每个cpp文件中都需要,不然就有可能无法检测出内存泄漏。这样把上面的预处理代码放到一个头文件中,这样只需要在每个cpp文件中包含这个头文件就可以了。假设这个头文件名为:common.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

在cpp文件中最好将#include”common.h”放到所#include指令的最后一个;以防后面包含的头文件会对malloc、free、new、delete进行重新定义。

本文参考:
浅谈C++中内存泄漏的检测

阅读全文
0 0
原创粉丝点击