检测原理
和其它一些内存泄漏检测的方式类似,debug_new 中提供了 operator new 重载,并使用
了宏在用户程序中进行替换。debug_new.h 中的相关部分如下:
void* operator new(size_t size, const char* file, int line);
void* operator new[](size_t size, const char* file, int line);
#define new DEBUG_NEW
#define DEBUG_NEW new(__FILE__, __LINE__)
拿上面加入 debug_new.h 包含后的 test.cpp 来说,"new char[10]"在预处理后会变成
"new("test.cpp", 4) char[10]",编译器会据此产生一个"operator new[](sizeof(char) * 10,
"test.cpp", 4)"调用。通过在 debug_new.cpp 中自定义"operator new(size_t, const char*, int)"和
"operator delete(void*)"(以及"operator new[]…"和"operator delete[]…";为避免行文累赘,以
下不特别指出,说到 operator new 和 operator delete 均同时包含数组版本),我可以跟踪所有
的内存分配调用,并在指定的检查点上对不匹配的 new 和 delete 操作进行报警。实现可以
相当简单,用 map 记录所有分配的内存指针就可以了:new 时往 map 里加一个指针及其对
应的信息,delete 时删除指针及对应的信息;delete 时如果 map 里不存在该指针为错误删除;
程序退出时如果 map 里还存在未删除的指针则说明有内存泄漏。
不过,如果不包含 debug_new.h,这种方法就起不了作用了。不仅如此,部分文件包含
debug_new.h,部分不包含 debug_new.h 都是不可行的。因为虽然我们使用了两种不同的
operator new --"operator new(size_t, const char*, int)"和"operator new(size_t)"-- 但可用的
"operator delete"还是只有一种!使用我们自定义的"operator delete",当我们删除由"operator
new(size_t)"分配的指针时,程序将认为被删除的是一个非法指针!我们处于一个两难境地:
要么对这种情况产生误报,要么对重复删除同一指针两次不予报警:都不是可接受的良好行
为。
看来,自定义全局"operator new(size_t)"也是不可避免的了。在 debug_new 中,我是这
样做的:
void* operator new(size_t size)
{
return operator new(size, "<Unknown>", 0);
}
但前面描述的方式去实现内存泄漏检测器,在某些 C++的实现中(如 GCC 2.95.3 中带
的 SGI STL)工作正常,但在另外一些实现中会莫名其妙地崩溃。原因也不复杂,SGI STL
使用了内存池,一次分配一大片内存,因而使利用 map 成为可能;但在其他的实现可能没
这样做,在 map 中添加数据会调用 operator new,而 operator new 会在 map 中添加数据,从
而构成一个死循环,导致内存溢出,应用程序立即崩溃。因此,我们不得不停止使用方便的
STL 模板,而使用手工构建的数据结构: