簡體   English   中英

嘗試包裝和覆蓋全局運算符new時的遞歸調用

[英]Recursive calls when trying to wrap and override the global operator new

我已經有一段時間沒有編程C ++了,並且在重載newdelete重載的全局運算符時遇到了一個奇怪的行為。 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我知道mallocfree會做,並且已經在發布到這里之前嘗試了它們(工作正常),但是,這種行為的背后是什么(以及如果我實際上將默認的operator new換成operator new怎么operator new ?:))?

如果您閱讀標准中的措辭,則在定義自己的函數時對全局operator new函數執行的operator new稱為替換 (不是重載覆蓋 )。 在大多數情況下,當人們談論更改全局operator new功能時,他們使用術語“超載”並將new操作員稱為“操作員”。 當最終的行為與操作員超載通常期望的結果不一致時,這通常會導致混亂。 這顯然是您的情況。 您期望重載行為,但是真正得到的卻是替換 :一旦在某個轉換單元中定義了全局operator new功能的一個替換版本,它將對整個程序起作用。 (而new並不是真正的運算符。)

作為附帶說明,從某種意義上說, 替換對於用戶通常不可用, 替換是這些罕見的“不公平”語言功能之一。 您不能在C ++中編寫“可替換”函數。 您無法編寫任何會像全局operator newoperator new默認庫實現那樣表現的行為。 (替換是弱符號的鏈接器概念的語言級別的體現)。

我認為在Neil的評論之后,沒有太多要添加的內容,但是如果您重載了new全局運算符,那么您不能只為一個翻譯單元執行此操作,對於整個可執行文件或dll, new將會被重載。

正如您已經注意到從您自己的重載全局運算符new中調用全局運算符new一樣,您將獲得無窮的遞歸(嗯,直到至少耗盡堆棧空間;)。

如果您想以此玩弄,請嘗試使用malloc和包裝器內部的free來啟動並運行。

如果操作員只在一個翻譯單元中是本地的,那么如果您將分配了一個new版本的對象的對象傳遞給另一翻譯單元,並嘗試使用另一個不匹配的操作員delete刪除它,則會遇到麻煩。

C ++標准的第18.4節介紹了操作符newdelete的詳細信息。

為了擴展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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM