不要让STL容器调用重载的new和delete运算符

3

我正在开发一个内存泄漏工具。在这个过程中,我正在重载new和delete运算符。它工作得很好。但是我要为其创建的代码大约有15000行。我不能更改现有的代码,只能将内存泄漏工具函数调用到现有代码中。现有代码包含STL容器(如列表、映射、栈等)。STL容器也调用new和delete运算符来分配或释放内存。我希望STL容器调用的是没有被重载的new和delete运算符,而不是已经被重载的new和delete运算符。

int *iptr = new int[10] ----> should call overloaded new[]
delete [] iptr -------------> should call overloaded delete[]
map.insert(10) -------------> should call default new[] ( which are in new.h)
map.erase()  ---------------> should call default delete[] ( which are in new.h)

我该怎么做呢?任何帮助都将不胜感激。

抱歉,我忘记提到我正在使用以下宏替换new和delete:

#define new DEBUG_NEW
#define DEBUG_NEW TrackMemory(__FILE__, __LINE__) ->* new
#define delete TrackDelete(__FILE__, __LINE__); delete

在这里,TrackMemory用于跟踪内存,而new用于分配内存,与delete相同。我的工具也很好用,但当涉及到STL容器时,它会给出错误的结果,因为它们只使用重载的new。请帮帮我。


3
为什么要重复造轮子?已经有许多非常好的工具可以做到这一点(如Valgrind等)。 - Nim
也许他正在使用Windows API? - C.J.
3个回答

3

先编写这些“空”函数。它们将替换标准的 new 和 delete 为你自己编写的函数:

void *operator new (size_t memorySize);
void *operator new[] (size_t memorySize);
void *operator new (size_t memorySize, const std::nothrow_t &) throw ();
void *operator new[] (size_t memorySize, const std::nothrow_t &) throw ();
void operator delete (void *memoryPointer);
void operator delete[] (void *memoryPointer);
void operator delete (void *memoryPointer, const std::nothrow_t &) throw ();
void operator delete[] (void *memoryPointer, const std::nothrow_t &) throw ();

然后编写单独的函数来分配和释放内存:
void *myNew (size-t memorySize);
void myDelete (void *memoryPointer);

在开始的函数中调用你的 myNew 和 myDelete。

通过使用 HeapAlloc 和 HeapFree 实现 myNew 和 myDelete。

然后创建一个全局变量并像这样标记它(这是 Visual Studio):

#pragma init_seg(lib)

这将确保您的全局变量首先初始化,最后清除。
到目前为止,我们已经涵盖了基础知识。要获得真正的泄漏报告功能,您需要在myNew函数中存储有关分配内存的信息。
使用全局变量的析构函数报告所有泄漏。
此外,您可以使用StackWalk获取调用堆栈,并将其与每个内存分配一起存储。
有些人可能会想知道为什么要这样做而不是使用其他工具:
  • 根据我的经验,在Visual Studio中进行泄漏报告是有限制的。它不会向您显示调用堆栈,而只会显示直接调用者,这使其毫无意义。
  • 一些工具使用#define替换new,delete,alloc等,但这在许多情况下会出现问题(例如,一个被称为free的类方法(在Qt中看到过),或者删除在头文件中完成,但在必须链接的.lib中进行了新的操作(也在Qt中看到过))。
  • 其他工具要求您对应用程序进行交互式快照,并在之后进行比较。在我上面的方法中,您将获得自动泄漏报告(无需手动操作,始终在应用程序结束时进行...)。
一旦您拥有自己的内存管理器,您就可以开始考虑添加其他功能(例如基于调用堆栈的内存统计信息,查找内存覆盖的技巧,查找在删除后重复使用内存的代码的技巧等)。

嗨,Patrick, 谢谢你的回复。我认为你建议的方法无法阻止STL容器调用我们重载的new和delete运算符。 - max_dev
没错,但通过替换全局的new和delete操作符,你可以确保整个STL也使用它们,并且你可以利用它来进行更好的内存泄漏报告,或者制作关于谁正在使用多少内存的统计。 - Patrick

2

15000行?嗯,将所有对newdelete的调用替换为代码中的包装方法(使用宏记录__FILE____LINE__可能是第一步),你很快就能完成。

或者使用现有工具,比如valgrind


这样,您将错过STL所做的所有分配。最好用自己的实现替换全局new/delete。 - Patrick
1
@Patrick:这正是OP所要求的,我想他只是假设他的STL正在工作。 - Matthieu M.

0

你说过载 operator new,但并不清楚你的意思。你不能像这样重载标准的 operator new,你只能在链接时替换它。当你替换它时,你将失去原来的那些,这是我见过的最愚蠢的设计。替代品也是全局的。你可以通过某些平台特定的链接器技巧(弱符号)解决这个问题。

你可以使用基于类的 operator new 进行一些查找技巧,当然你也可以对放置参数进行 operator new 的重载,但我假设你指的都不是这些。


实际上,您可以提供自己的定义或 newdelete,在这种情况下,编译器将使用您的定义而不是默认定义。或者我弄错了吗? - Matthieu M.
用自己的实现替换标准 new 和 delete 并不是完全愚蠢的。事实上,如果你想编写自己的内存分配器,或者想使用另一个(比如 DougLea 的内存分配器),而且你不想使用平台特定的技巧,比如改变 DLL 入口表(跳板等),那么这是最好的方法。 - Patrick
@Matthieu:你说得完全正确,但我必须停止这件事。我只想让STL容器使用默认的new和delete运算符,而不是我的。 - max_dev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接