VC
++6.0
中内存泄漏检测
摘自:VC知识库BLOG-周星星 网址:blog.vckbase.com/bruceteen/
对 C++ 代码而言 , 内存泄漏问题虽然有诸多方法避免 , 但实际代码编写的时候 , 或出于自信或出于复杂性的考虑 , 常常还会用到原始的 operator new, 这不可避免的会带来内存泄漏的可能 , 不久前本人因为违反了 " 可用于被多态继承的基类其析构函数应当有 virtual 修饰 " 的法则 ( 一不小心就忘了写 virtual ^_^ ), 导致了内存泄漏 , 因此我觉得出于安全考虑 , 在代码中加入内存泄漏检查机制还是很必要的 , 也因为这次的内存泄漏事件促使我写出这一篇文章 .
VC++ 中本身就有内存泄漏检查的机制 , 你可以在向导生成的支持 MFC 的工程中看到如下代码 :
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
通过它们 , 你能非常容易的发现代码中的内存泄漏 , 但是如果手工将这个功能移植到非 MFC 工程中去是很繁琐的一件事 , 另外它还有一个 bug, 在多线程并发调用这个 DEBUG_NEW 时会导致系统级错误 , 因此本人在此重写了这个功能 , 将以下的 debug_new.h 和 debug_new.cpp 添加到工程中 , 并在需要检测的 cpp 中 #include "debug_new.h" 和 main 中一开始处加入 REG_DEBUG_NEW 宏即可 .
1. debug_new.h 源代码
/************************************************************************/
/* comment: 此文件与 debug_new.cpp 配合使用 , 用于在调试期发现内存泄漏 */
/* 仅在 VC++ 编译器中适用 ( 包括 Intel C++, 因为它使用了相同的库 ) */
/* 作者 : 周星星 http://blog.vckbase.com/bruceteen/ */
/* 版权申明 : 无 , 可任意 使用 , 修改 和 发布 */
/************************************************************************/
/* sample
#include <iostream>
#include "debug_new.h" // +
using namespace std;
int main( void )
{
REG_DEBUG_NEW; // +
char* p = new char[2];
cout << "--End--" << endl;
return 0;
}
在 VC++ IDE 中按 F5 调试运行将会在 Output 窗口的 Debug 页看到类似如下的提示 :
Dumping objects ->
d:/test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
Data: < > CD CD
Object dump complete.
如果不出现如上提示请 Rebuild All 一次 .
*/
#ifndef _DEBUG_NEW_H_
#define _DEBUG_NEW_H_
#ifdef _DEBUG
#undef new
extern void _RegDebugNew( void );
extern void* __cdecl operator new( size_t, const char*, int );
extern void __cdecl operator delete( void*, const char*, int);
#define new new(__FILE__, __LINE__)
#define REG_DEBUG_NEW _RegDebugNew();
#else
#define REG_DEBUG_NEW
#endif // _DEBUG
#endif // _DEBUG_NEW_H_
/* comment: 此文件与 debug_new.cpp 配合使用 , 用于在调试期发现内存泄漏 */
/* 仅在 VC++ 编译器中适用 ( 包括 Intel C++, 因为它使用了相同的库 ) */
/* 作者 : 周星星 http://blog.vckbase.com/bruceteen/ */
/* 版权申明 : 无 , 可任意 使用 , 修改 和 发布 */
/************************************************************************/
/* sample
#include <iostream>
#include "debug_new.h" // +
using namespace std;
int main( void )
{
REG_DEBUG_NEW; // +
char* p = new char[2];
cout << "--End--" << endl;
return 0;
}
在 VC++ IDE 中按 F5 调试运行将会在 Output 窗口的 Debug 页看到类似如下的提示 :
Dumping objects ->
d:/test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
Data: < > CD CD
Object dump complete.
如果不出现如上提示请 Rebuild All 一次 .
*/
#ifndef _DEBUG_NEW_H_
#define _DEBUG_NEW_H_
#ifdef _DEBUG
#undef new
extern void _RegDebugNew( void );
extern void* __cdecl operator new( size_t, const char*, int );
extern void __cdecl operator delete( void*, const char*, int);
#define new new(__FILE__, __LINE__)
#define REG_DEBUG_NEW _RegDebugNew();
#else
#define REG_DEBUG_NEW
#endif // _DEBUG
#endif // _DEBUG_NEW_H_
2. debug_new.cpp 源代码
/************************************************************************/
/* comment: 此文件与 debug_new.h 配合使用 , 用于在调试期发现内存泄漏 */
/* 仅在 VC++ 编译器中适用 ( 包括 Intel C++, 因为它使用了相同的库 ) */
/* 作者 : 周星星 http://blog.vckbase.com/bruceteen/ */
/* 版权申明 : 无 , 可任意 使用 , 修改 和 发布 */
/************************************************************************/
//#include "debug_new.h"
#ifdef _DEBUG
#include <windows.h>
#include <crtdbg.h>
class _CriSec
{
CRITICAL_SECTION criSection;
public:
_CriSec() { InitializeCriticalSection( &criSection ); }
~_CriSec() { DeleteCriticalSection( &criSection ); }
void Enter() { EnterCriticalSection( &criSection ); }
void Leave() { LeaveCriticalSection( &criSection ); }
} _cs;
void _RegDebugNew( void )
{
_CrtSetDbgFlag( _CRTDBG_REPORT_FLAG | _CRTDBG_LEAK_CHECK_DF );
}
void* __cdecl operator new( size_t nSize, const char* lpszFileName, int nLine )
{
// comment 1: MFC 中提供的 debug new 虽然加了锁 , 但我在实际测试的时候发现多线程并发
// 调用的时候还是抛出了系统错误 , 所以我在这里加了一个线程互斥量 .
// comment 2: debug new 和 debug delete 之间需不需要互斥我并不知道 , 保险起见 , 我同样
// 加了线程互斥量 .
// comment 3: 按照 C++ 标准规定 , 在 operator new 失败后应当调用 set_new_handler 设置的
// 函数 , 但是 MSDN 中却说 " 头文件 new 中的 set_new_handler 是 stub 的 , 而应该使
// 用头文件 new.h 中的 _set_new_handler", 这简直是滑天下之大稽 .
// 以下是 VC++6.0 中的 set_new_handler 定义 :
// new_handler __cdecl set_new_handler( new_handler new_p )
// {
// assert( new_p == 0 ); // cannot use stub to register a new handler
// _set_new_handler( 0 );
// return 0;
// }
// 所以我也无计可施 , 只能舍弃 set_new_handler 的作用 .
_cs.Enter();
void* p = _malloc_dbg( nSize, _NORMAL_BLOCK, lpszFileName, nLine );
_cs.Leave();
return p;
}
void __cdecl operator delete( void* p, const char* /*lpszFileName*/, int /*nLine*/ )
{
_cs.Enter();
_free_dbg( p, _CLIENT_BLOCK );
_cs.Leave();
}
#endif
/* comment: 此文件与 debug_new.h 配合使用 , 用于在调试期发现内存泄漏 */
/* 仅在 VC++ 编译器中适用 ( 包括 Intel C++, 因为它使用了相同的库 ) */
/* 作者 : 周星星 http://blog.vckbase.com/bruceteen/ */
/* 版权申明 : 无 , 可任意 使用 , 修改 和 发布 */
/************************************************************************/
//#include "debug_new.h"
#ifdef _DEBUG
#include <windows.h>
#include <crtdbg.h>
class _CriSec
{
CRITICAL_SECTION criSection;
public:
_CriSec() { InitializeCriticalSection( &criSection ); }
~_CriSec() { DeleteCriticalSection( &criSection ); }
void Enter() { EnterCriticalSection( &criSection ); }
void Leave() { LeaveCriticalSection( &criSection ); }
} _cs;
void _RegDebugNew( void )
{
_CrtSetDbgFlag( _CRTDBG_REPORT_FLAG | _CRTDBG_LEAK_CHECK_DF );
}
void* __cdecl operator new( size_t nSize, const char* lpszFileName, int nLine )
{
// comment 1: MFC 中提供的 debug new 虽然加了锁 , 但我在实际测试的时候发现多线程并发
// 调用的时候还是抛出了系统错误 , 所以我在这里加了一个线程互斥量 .
// comment 2: debug new 和 debug delete 之间需不需要互斥我并不知道 , 保险起见 , 我同样
// 加了线程互斥量 .
// comment 3: 按照 C++ 标准规定 , 在 operator new 失败后应当调用 set_new_handler 设置的
// 函数 , 但是 MSDN 中却说 " 头文件 new 中的 set_new_handler 是 stub 的 , 而应该使
// 用头文件 new.h 中的 _set_new_handler", 这简直是滑天下之大稽 .
// 以下是 VC++6.0 中的 set_new_handler 定义 :
// new_handler __cdecl set_new_handler( new_handler new_p )
// {
// assert( new_p == 0 ); // cannot use stub to register a new handler
// _set_new_handler( 0 );
// return 0;
// }
// 所以我也无计可施 , 只能舍弃 set_new_handler 的作用 .
_cs.Enter();
void* p = _malloc_dbg( nSize, _NORMAL_BLOCK, lpszFileName, nLine );
_cs.Leave();
return p;
}
void __cdecl operator delete( void* p, const char* /*lpszFileName*/, int /*nLine*/ )
{
_cs.Enter();
_free_dbg( p, _CLIENT_BLOCK );
_cs.Leave();
}
#endif
3. 事例代码
#include <iostream>
#include "debug_new.h"
using namespace std;
int main( void )
{
REG_DEBUG_NEW;
char* p = new char[2];
p[0] = 'A';
p[1] = 'B';
cout << "--End--" << endl;
return 0;
}
#include "debug_new.h"
using namespace std;
int main( void )
{
REG_DEBUG_NEW;
char* p = new char[2];
p[0] = 'A';
p[1] = 'B';
cout << "--End--" << endl;
return 0;
}
4. 结果输出
在
VC++ IDE
中按
F5
调试运行将会在
Output
窗口的
Debug
页看到类似如下的提示
:
……
Dumping objects ->
d:/test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
Data: <AB> 41 42
Object dump complete.
……
……
Dumping objects ->
d:/test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
Data: <AB> 41 42
Object dump complete.
……