MinGW 中的全局重载运算符 new/delete
Global overload operator new/delete in MinGW
我想在我的应用程序中重载 new/delete 运算符以捕获所有内存泄漏。它适用于 Linux 正常。但是我在 Windows 上遇到了问题。 New/delete 重载仅适用于 .exe 但不适用于来自 .dll 文件的调用。此外,如果某些对象是在我的代码中创建的,但正在从 .dll 文件中删除,则会导致应用程序崩溃。 Cppreference here 说
Versions (1-8) are replaceable: a user-provided non-member function
with the same signature defined anywhere in the program, in any source
file, replaces the default version. Its declaration does not need to
be visible.
我编写了最小的 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. 我写了 minimal 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
Windows 不是 Linux,您需要做出相应的行为。
简单地说,确保每个 EXE/DLL 管理自己的内存通常是安全的,即由 EXE/DLL 分配的内存只能由相同的 [=20= 释放].这意味着当一个 DLL 提供 createObj
功能时,它还应该提供 destroyObj
功能。当然,如果可以确保所有 EXE 和 DLL 使用相同的运行时 DLL(相同版本,并且没有静态运行时),则不必执行此操作。即使这样,EXE 和 DLL 也不会共享它们的 operator new
/delete
.
使用内存调试器时,您应该link将其目标文件添加到每个 EXE 和 DLL 中。每个 EXE/DLL 将拥有自己的内存分配器并自行检测。
我在 GCC Bugzilla 上找到了答案 – Bug 77726。
刘浩写道:
It isn't weired if you know how a Dynamic-Link Library (DLL) on
Windows works different than a Shared Object (SO) on Linux.
Windows does not have a dynamic linker such as ld.so
on Linux.
Symbols in a DLL are resolved at build-time, in contrary to Linux,
where symbols in a SO are resolved at load-time. The DLL loader can
resolve symbols to addresses, but it isn't as powerful as linkers
after all. Consequently, an executable cannot use its strong symbols
to override already-resolved weak ones in a DLL.
If the user does not define a sized deallocation function, the default
one in libstdc++*.dll is used, which calls the weak, default,
non-sized deallocation function in the same DLL, which is the only
candidate when the DLL is built and can't be overridden.
我想在我的应用程序中重载 new/delete 运算符以捕获所有内存泄漏。它适用于 Linux 正常。但是我在 Windows 上遇到了问题。 New/delete 重载仅适用于 .exe 但不适用于来自 .dll 文件的调用。此外,如果某些对象是在我的代码中创建的,但正在从 .dll 文件中删除,则会导致应用程序崩溃。 Cppreference here 说
Versions (1-8) are replaceable: a user-provided non-member function with the same signature defined anywhere in the program, in any source file, replaces the default version. Its declaration does not need to be visible.
我编写了最小的 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. 我写了 minimal 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
Windows 不是 Linux,您需要做出相应的行为。
简单地说,确保每个 EXE/DLL 管理自己的内存通常是安全的,即由 EXE/DLL 分配的内存只能由相同的 [=20= 释放].这意味着当一个 DLL 提供 createObj
功能时,它还应该提供 destroyObj
功能。当然,如果可以确保所有 EXE 和 DLL 使用相同的运行时 DLL(相同版本,并且没有静态运行时),则不必执行此操作。即使这样,EXE 和 DLL 也不会共享它们的 operator new
/delete
.
使用内存调试器时,您应该link将其目标文件添加到每个 EXE 和 DLL 中。每个 EXE/DLL 将拥有自己的内存分配器并自行检测。
我在 GCC Bugzilla 上找到了答案 – Bug 77726。
刘浩写道:
It isn't weired if you know how a Dynamic-Link Library (DLL) on Windows works different than a Shared Object (SO) on Linux.
Windows does not have a dynamic linker such as
ld.so
on Linux. Symbols in a DLL are resolved at build-time, in contrary to Linux, where symbols in a SO are resolved at load-time. The DLL loader can resolve symbols to addresses, but it isn't as powerful as linkers after all. Consequently, an executable cannot use its strong symbols to override already-resolved weak ones in a DLL.If the user does not define a sized deallocation function, the default one in libstdc++*.dll is used, which calls the weak, default, non-sized deallocation function in the same DLL, which is the only candidate when the DLL is built and can't be overridden.