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的主要内容,如果未能解决你的问题,请参考以下文章

C ++全局重载运算符= [重复]

运算符重载

流插入运算符为什么要被重载为全局函数?

运算符重载

嵌入式c++加号运算符重载

C++学习32 重载new和delete运算符