MinGW 中的全局重载运算符 new/delete
Posted
技术标签:
【中文标题】MinGW 中的全局重载运算符 new/delete【英文标题】:Global overload operator new/delete in MinGW 【发布时间】:2017-09-03 16:57:04 【问题描述】:我想在我的应用程序中重载新/删除运算符以捕获所有内存泄漏。它在 Linux 上运行良好。但是我在 Windows 上遇到了问题。新/删除重载仅适用于 .exe,但不适用于来自 .dll 文件的调用。此外,如果在我的代码中创建了某个对象但从 .dll 文件中删除它会导致应用程序崩溃。 Cppreference here 说
版本 (1-8) 可替换:用户提供的非成员函数 在程序的任何地方、任何来源中定义的相同签名 文件,替换默认版本。它的声明不需要 可见。
我编写了最小的 Qt 模板应用程序来测试它。这里mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <cstdio>
#include <cstdlib>
// replacement of a minimal set of functions:
void *operator new(std::size_t sz)
void *ptr = std::malloc(sz);
std::printf("global op new called, size = %zu, pointer = 0x%p\n", sz, ptr);
return ptr;
void operator delete(void* ptr) noexcept
std::printf("global op delete called, pointer = 0x%p\n", ptr);
std::free(ptr);
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
ui->setupUi(this);
MainWindow::~MainWindow()
delete ui;
输出:
global op new called, size = 20, pointer = 0x00c4f608
global op new called, size = 24, pointer = 0x00c4f648
global op new called, size = 16, pointer = 0x00b35bf8
global op new called, size = 24, pointer = 0x00c4f6a8
global op new called, size = 24, pointer = 0x00c4f868
global op new called, size = 24, pointer = 0x00c4f988
global op delete called, pointer = 0x00c4f608
已使用 Qt 4.8.7/GCC 4.8.2 和 Qt 5.5.1/GCC 4.9.2 进行了测试。那么如何在 MinGW 中全局重载 new/delete 呢?
P。 S. 我写了最少的test case 来重现这个问题。它输出我
$ ./main.exe
global op new called, size = 4, pointer = 0x003e17b8
global op new called, size = 4, pointer = 0x003e3d68
library delete called, pointer = 0x003e17b8
global op delete called, pointer = 0x003e3d68
【问题讨论】:
对于 EXE/DLL 问题,您需要确保您使用的是共享 DLL C 运行时支持。如果不是,那么您将有 2 个单独的堆(每个模块中一个),并且其中一个的malloc
不能是另一个的 free
。
我建议避免在 dll 中释放内存。最好将释放内存的责任分配给分配它的组件。
jumper0x08,这是无法避免的。删除父级时删除所有子级,这是 Qt 完全正确的行为。模板中的最小 Qt 应用程序 (.exe) 创建对象层次结构并仅删除根对象。子项将在 Qt 库 (.dll) 中被删除。
Richard,这不是 malloc/free 的问题。这是运营商新/运营商删除问题。
如果你没有共享 CRT 那么你有两个堆并且 new/delete 将不起作用。
【参考方案1】:
我在 GCC Bugzilla 上找到了答案 - Bug 77726。
刘昊写道:
如果您知道动态链接库 (DLL) 如何在 Windows 与 Linux 上的共享对象 (SO) 不同。
Windows 在 Linux 上没有动态链接器,例如
ld.so
。 与 Linux 不同,DLL 中的符号在构建时解析, SO中的符号在加载时解析。 DLL 加载器可以 将符号解析为地址,但不如链接器强大 毕竟。因此,可执行文件不能使用其强符号 覆盖 DLL 中已解决的弱项。如果用户没有定义一个大小的释放函数,默认 使用 libstdc++*.dll 中的一个,它调用弱,默认, 同一个 DLL 中的非大小释放函数,这是唯一的 DLL 已构建且无法被覆盖时的候选对象。
【讨论】:
【参考方案2】:Windows 不是 Linux,您需要相应地进行操作。
简单地说,确保每个 EXE/DLL 管理自己的内存通常是安全的,即由 EXE/DLL 分配的内存只能由同一个 EXE/DLL 释放。这意味着当一个DLL提供createObj
函数时,它也应该提供一个destroyObj
函数。当然,如果您可以确保所有 EXE 和 DLL 使用相同的运行时 DLL(相同版本,并且没有静态运行时),则不必这样做。即使这样,EXE 和 DLL 也不会共享它们的 operator new
/delete
。
使用内存调试器时,您应该将其目标文件链接到每个 EXE 和 DLL。然后每个 EXE/DLL 将拥有自己的内存分配器并自行检测。
【讨论】:
以上是关于MinGW 中的全局重载运算符 new/delete的主要内容,如果未能解决你的问题,请参考以下文章