C++--问题27--如何检测内存泄漏
1.内存泄露的定义:
动态分配内存所开辟的空间,在使用完毕后未手动释放,导致一直占据该内存,即为内存泄漏。
2.造成内存泄漏的几种原因:
(1)类的构造函数和析构函数中new和delete没有配套。
(2)在释放对象数组时没有使用delete[],使用了delete。
(3)没有将基类的析构函数定义为虚函数,当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确释放,因此造成内存泄露。
(4)没有正确的清楚嵌套的对象指针。
3.在VS中检测内从泄漏:CRT
方法一:
#define CRTDBG_MAP_ALLOC //放在程序最前
#include <iostream>
#include <stdlib.h>
#include <crtdbg.h>
using namespace std;
int main()
{
int* a = new int[10];
int* p = new int[1000];
_CrtDumpMemoryLeaks(); //放在程序最后
system("pause");
return 0;
}
点击“调试”,然后在输出窗口可以看到:
注意:{}中的数字指明这块内存是程序中总计第几个被申请的,这种方法没有行号和其他信息输出。
要行号我们可以定义,代码如下:
#ifdef _DEBUG
#define New new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
//在入口函数中包含 _CrtDumpMemoryLeaks();
//即可检测到内存泄露
//以如下测试函数为例:
int main()
{
char* pChars = New char[10];
_CrtDumpMemoryLeaks();
return 0;
}
{77}和{76}代表了第77、76次内存分配操作发生了泄漏,所以根据这两个信息,可以定位到内存泄漏的位置,可以添加如下代码:
#define CRTDBG_MAP_ALLOC
#include <iostream>
#include <stdlib.h>
#include <crtdbg.h>
using namespace std;
int main()
{
_CrtSetBreakAlloc(77);
//_CrtSetBreakAlloc(76); //分别取消注释即可触发所有断点。
int* a = new int[10];
int* p = new int[1000];
_CrtDumpMemoryLeaks();
system("pause");
return 0;
}
在VS2019中会输出结果如下:
你发现,程序运行到int* p = new int[1000]; 一句时,自动停下来进入调试状态。细细体会一下,你可以发现,这种方式你获得的信息远比在程序退出时获得文件名及行号有价值得多。因为报告泄漏文件名及行号,你获得的只是静态的信息,然而_CrtSetBreakAlloc则是把整个现场恢复,你可以通过对函数调用栈分析(我发现很多人不习惯看函数调用栈,如果你属于这种情况,强烈推荐你去补上这一课,因为它太重要了)以及其他在线调试技巧,来分析产生内存泄漏的原因。通常情况下,这种分析方法可以在5分钟内找到肇事者。
方法二:在入口函数中包含 _CrtDumpMemoryLeaks();
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
//在入口函数中包含 _CrtDumpMemoryLeaks();
//即可检测到内存泄露
//定义函数:
inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}
//该函数可以放在主函数的任意位置,都能正确的触发内存泄露输出
//以如下测试函数为例:
int main()
{
EnableMemLeakCheck();
char* pChars = new char[10];
//_CrtDumpMemoryLeaks();
return 0;
}
输出结果: