2017-09-03 33 views
0

我想在應用程序中重載新的/刪除操作符來捕獲所有內存泄漏。它在Linux上很好。但是我在Windows上遇到了問題。新/刪除重載僅適用於.exe,但不適用於來自.dll文件的調用。此外,如果某個對象是在我的代碼中創建的,但是從.dll文件中刪除,則會導致應用程序崩潰。 Cppreference hereMinGW中的全局超載操作符new/delete

版本(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 
+0

對於EXE/DLL問題,您需要確保您使用的是共享DLL C運行時支持。如果你不是,那麼你將有2個獨立的堆(每個模塊中有一個堆),而一個malloc不能從另一箇中'免費'。 –

+0

我建議避免在dll中釋放內存。最好將分配內存的責任分配給分配內存的組件。 – jumper0x08

+0

jumper0x08,這是無法避免的。刪除父母時刪除所有孩子對於Qt完全正確的行爲。模板中的最小Qt應用程序(.exe)創建對象層次結構並僅刪除根對象。孩子們將被刪除在Qt庫(.dll)中。 –

回答

0

我在GCC Bugzilla上找到了答案 - Bug 77726

劉昊寫道:

如果你知道一個動態鏈接庫(DLL)上的Windows 如何工作不是共享對象(SO)在Linux上不同它不weired。

Windows在Linux上沒有動態鏈接器,如ld.so。 DLL中的符號在構建時解析,與Linux相反, 中SO中的符號在加載時解析。畢竟,DLL加載器可以將符號解析爲地址,但它不如連接器 那麼強大。因此,可執行文件無法使用其強符號 覆蓋DLL中已解決的弱符號。

如果用戶沒有定義大小的釋放函數,默認 一個在libstdC++ *中。dll被使用,它在同一個DLL中調用弱,默認, 非大小的釋放函數,這是構建DLL時不可覆蓋的唯一 候選者。

0

Windows不是Linux,你需要相應的行爲。

簡而言之,確保每個EXE/DLL管理自己的內存通常是安全的,即由EXE/DLL分配的內存只能由同一個EXE/DLL重新分配。這意味着當一個DLL提供createObj函數時,它也應該提供一個destroyObj函數。當然,如果可以確保所有EXE和DLL使用相同的運行時DLL(相同版本,並且沒有靜態運行時),則不必執行此操作。即使這樣,EXE和DLL也不會共享他們的operator new/delete

使用內存調試器時,應該將其對象文件鏈接到每個EXE和DLL。然後每個EXE/DLL將擁有自己的內存分配器並自行檢測。