![](/img/trans.png)
[英]LNK2005 Error when trying to override global new and delete operators
[英]Recursive calls when trying to wrap and override the global operator new
我已經有一段時間沒有編程C ++了,並且在重載new
和delete
重載的全局運算符時遇到了一個奇怪的行為。 source file nevertheless calls an operator new
overloaded in another (and separately compiled so) source file. 問題的實質似乎是包裝器圍繞默認的全局new
構建,並且駐留在源文件中,但是仍然調用operator new
,該operator new
在另一個(並因此單獨編譯)的源文件中被重載。
為什么會這樣,即我違反/濫用了哪些語言規則/功能?
預先感謝,詳細信息如下。
項目結構:
.
..
main.cpp
mem_wrappers.h
mem_wrappers.cpp
項目文件內容:
main.cpp
#include "./mem_wrappers.h"
#include <iostream>
#include <cstring>
void* operator new[] (size_t sz) throw (std::bad_alloc) {
std::cout << "overloaded new()[]" << std::endl;
return default_arr_new_wrapper(sz);
}
int main() {
const unsigned num = 5;
int * i_arr = new int [num];
return 0;
}
mem_wrappers.h
#include <cstring>
void * default_arr_new_wrapper(size_t sz);
mem_wrappers.cpp
#include <new>
#include <cstring>
#include <iostream>
void * default_arr_new_wrapper(size_t sz) {
std::cout << "default_arr_new wrapper()" << std::endl;
return ::operator new[](sz);
}
when run gives endless operator new[]
to default_arr_new_wrapper
and vice versa calls resulting in the following output: 與 ,運行時將無限operator new[]
賦予default_arr_new_wrapper
,反之亦然,從而產生以下輸出:
overloaded new()[]
default_arr_new_wrapper()
overloaded new()[]
default_arr_new_wrapper()
...
最后,在SO中(MS Visual Studio Express編譯器的行為類似)。
我使用的編譯器: gcc 3.4.2版(mingw-special)和MS Visual Studio 2008版本9.0.30729.1 SP 。
編輯/更新
如果(如第一個答案和評論所建議的那樣)僅通過在單個編譯單元中重載全局operator new
[操作符]就為整個可執行文件有效地重新定義了全局operator new
,則:
僅僅是與目標文件鏈接,該文件的源會使全局operator new
過載,任何operator new
應用程序或庫都會更改其內存分配策略(可這樣稱呼)? 因此,這個重載運算符是否有效地為語言運行時提供了一個鈎子(我的意思是,已經編譯且沒有任何重載的新目標文件中的那些鏈接器符號是什么,難道只能有一個new
)?
PS我知道malloc
和free
會做,並且已經在發布到這里之前嘗試了它們(工作正常),但是,這種行為的背后是什么(以及如果我實際上將默認的operator new
換成operator new
怎么operator new
?:))?
如果您閱讀標准中的措辭,則在定義自己的函數時對全局operator new
函數執行的operator new
稱為替換 (不是重載和覆蓋 )。 在大多數情況下,當人們談論更改全局operator new
功能時,他們使用術語“超載”並將new
操作員稱為“操作員”。 當最終的行為與操作員超載通常期望的結果不一致時,這通常會導致混亂。 這顯然是您的情況。 您期望重載行為,但是真正得到的卻是替換 :一旦在某個轉換單元中定義了全局operator new
功能的一個替換版本,它將對整個程序起作用。 (而new
並不是真正的運算符。)
作為附帶說明,從某種意義上說, 替換對於用戶通常不可用, 替換是這些罕見的“不公平”語言功能之一。 您不能在C ++中編寫“可替換”函數。 您無法編寫任何會像全局operator new
和operator new
默認庫實現那樣表現的行為。 (替換是弱符號的鏈接器概念的語言級別的體現)。
我認為在Neil的評論之后,沒有太多要添加的內容,但是如果您重載了new全局運算符,那么您不能只為一個翻譯單元執行此操作,對於整個可執行文件或dll, new
將會被重載。
正如您已經注意到從您自己的重載全局運算符new中調用全局運算符new一樣,您將獲得無窮的遞歸(嗯,直到至少耗盡堆棧空間;)。
如果您想以此玩弄,請嘗試使用malloc
和包裝器內部的free
來啟動並運行。
如果操作員只在一個翻譯單元中是本地的,那么如果您將分配了一個new
版本的對象的對象傳遞給另一翻譯單元,並嘗試使用另一個不匹配的操作員delete
刪除它,則會遇到麻煩。
C ++標准的第18.4節介紹了操作符new
和delete
的詳細信息。
為了擴展Neil的評論,“ global” new可能不會被賦予static
存儲類說明符,因此它是“ global”的-在模塊外部的鏈接器可見(例如,可以從任何其他模塊中替換) )。
無效:
static void* operator new(size_t sz) {}
有效:
//in main.cpp
void* operator new(size_t sz) {}
//in foo.cpp
extern void* operator new(size_t sz);
根據您要執行的操作,最好的方法可能是在定義中使用malloc / new,或在替代中添加參數,以便簽名更改(然后可以遞歸回默認的全局new,而無需遞歸)。
不要忘記在分配失敗的情況下引發異常,並實現無拋出版本的重載。
為了擴大AndreyT所說的話:
當您編寫自己的全局operator new
函數時,您將提供一個鏈接器可以看到的定義。 當涉及鏈接時間時,鏈接器將需要為new
定義一個定義,只是因為它使用了很多東西。 它開始在您要鏈接的目標文件中進行搜索,而且請放心,它可以停止搜索。
如果您未提供定義,則它將繼續搜索您的任何其他目標文件,然后搜索您提供的庫,最后搜索編譯器附帶的運行時,編譯器必須提供一個或不提供鏈接。
在GNU C ++標准庫中,您會看到類似這樣的修飾,意思是“弱鏈接”:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) throw (std::bad_alloc)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.