2017-08-06 60 views
9

C++參考頁面列出了針對global new operators的8 類特定過載。其中四個是爲2017版C++添加的。如果我爲一個類編寫運算符new和delete,是否必須編寫所有的重載?

類專用分配功能

void* T::operator new (std::size_t count); 
void* T::operator new[](std::size_t count); 
void* T::operator new (std::size_t count, std::align_val_t al); // (since C++17) 
void* T::operator new[](std::size_t count, std::align_val_t al); // (since C++17) 

類專用配置分配函數

void* T::operator new (std::size_t count, user-defined-args...); 
void* T::operator new[](std::size_t count, user-defined-args...); 
void* T::operator new (std::size_t count, 
    std::align_val_t al, user-defined-args...); // (since C++17) 
void* T::operator new[](std::size_t count, 
    std::align_val_t al, user-defined-args...); // (since C++17) 

該網站還列出了10個類特定版本global delete operators,其中4人介紹了2017年。

類特定的通常釋放函數

void T::operator delete (void* ptr); 
void T::operator delete[](void* ptr); 
void T::operator delete (void* ptr, std::align_val_t al); // (since C++17) 
void T::operator delete[](void* ptr, std::align_val_t al); // (since C++17) 
void T::operator delete (void* ptr, std::size_t sz); 
void T::operator delete[](void* ptr, std::size_t sz); 
void T::operator delete (void* ptr, std::size_t sz, std::align_val_t al); // (since C++17) 
void T::operator delete[](void* ptr, std::size_t sz, std::align_val_t al); // (since C++17) 

類專用放置釋放函數

void T::operator delete (void* ptr, args...); 
void T::operator delete[](void* ptr, args...); 

如果我寫了與new和delete操作一個C++類,做我需要重載那些所有?我忽略了可替換的全球運營商,因爲我只寫類專用運營商。

This other question provides info on writing ISO compliant new and delete operators,但並沒有說我是否應該重載所有這些,或只是一些。

this question about class specific new and delete operators的回答並未說明是否全部或部分替換。

如果您可以提供來自C++標準的引文或C++內存專家的評論,這將有所幫助。

+1

對我來說似乎[這個答案](https://stackoverflow.com/a/7151831/366904)有你正在尋找的信息。 –

+2

好吧,如果你想使用自定義分配方案,我認爲對所有進行實際分配和釋放操作的操作符進行重載是謹慎的。那,或者明確地刪除那些你不想支持的。 – StoryTeller

+0

你可以使用'template'來定義'new'和'delete'的位置。這將允許編譯器生成類型專用代碼。 –

回答

2

不,你不需要寫新的所有變化和delete操作符爲你的類。

有多種原因可以選擇某些版本的新版本,而不是其他版本。我將分別描述每個原因。

幾乎總是比較喜歡那些沒有尺寸參數的刪除操作符。

當我寫刪除運營商的基類,它提供的內存處理對於其它類,我使用這些版本的刪除操作符

void T::operator delete (void* ptr, std::size_t sz); 
void T::operator delete[](void* ptr, std::size_t sz); 
void T::operator delete (void* ptr, std::size_t sz, std::align_val_t al); // (since C++17) 
void T::operator delete[](void* ptr, std::size_t sz, std::align_val_t al); // (since C++17) 

,故意省略或=delete這些版本。

void T::operator delete (void* ptr); 
void T::operator delete[](void* ptr); 
void T::operator delete (void* ptr, std::align_val_t al); // (since C++17) 
void T::operator delete[](void* ptr, std::align_val_t al); // (since C++17) 

原因是std::size_t sz參數告訴我對象的大小或數組的大小。當我編寫我的基類時,我無法知道派生類的對象的大小,所以使用size參數會有所幫助。我的一些內存處理程序按大小分隔對象(當所有的塊大小相同時,更容易集中內存)。我可以使用size參數來快速選擇要搜索的內存池,而不是搜索所有內存池。這將O(n)算法變成O(1)動作。

我的一些內存分配器使用「鏈模型」而不是「塊模型」,並且size參數也有助於在那裏刪除。 (如果它預先分配了一個大塊,然後將該塊分割成單獨的塊,就像一個數組,我稱之爲「塊模型」,如果每個塊指向前一個塊和下一個塊,我稱之爲「鏈模型」鏈表或鏈)。因此,當有人從內存塊鏈中刪除塊時,我想讓刪除操作員知道被刪除的塊是正確的大小。我可以在斷言(下一個塊的大小==地址 - 這個塊的地址)的刪除操作中放置一個斷言。

在適當情況下,優先選擇具有對齊參數的新操作符和刪除操作符。

既然C++ 17爲新的操作符提供了一個對齊參數,如果您需要它們,請使用它們。如果您需要性能,請將對象對齊在4,8或16個字節邊界上,這樣做!它使程序更快一點。

假設你有一個對齊感知的內存分配器。它知道一些對象最好存儲在4字節的邊界上,因爲這些對象很小,如果使用4字節的邊界,則可以將更多內容擠入內存。它也知道一些對象最好在8字節的邊界上對齊,因爲這些對象經常被使用。

如果內存處理程序提供了正確的新運算符,並且派生類爲對齊提供了正確的值,則它將知道這一點。

的2017 C++標準表示:

當分配對象,它們的對準超過STDCPP_DEFAULT_NEW_ALIGNMENT的對象和數組,執行超載分辨率的兩倍:首先,用於對準感知函數簽名,然後對於對齊 - 不知道功能簽名。這意味着如果具有擴展對齊的類具有不需要對齊的特定於類的分配函數,那麼將調用該函數,而不是全局對齊感知分配函數。這是故意的:班級成員應該知道如何處理這個班級。

這意味着編譯器將檢查具有對齊參數的新操作符和刪除操作符,然後檢查沒有對齊參數的操作符。

如果你有一個對齊感知的內存句柄,那麼總是提供這些新的操作符,即使你也想給你的客戶端代碼忽略對齊的選項。

void* T::operator new (std::size_t count, std::align_val_t al); // (since C++17) 
void* T::operator new[](std::size_t count, std::align_val_t al); // (since C++17) 
void* T::operator new (std::size_t count, 
    std::align_val_t al, user-defined-args...); // (since C++17) 
void* T::operator new[](std::size_t count, 
    std::align_val_t al, user-defined-args...); // (since C++17) 

您可以強制代碼以提供校準參數,如果你提供上述新的運營商和省略或=delete這些重載。

void* T::operator new (std::size_t count); 
void* T::operator new[](std::size_t count); 

void* T::operator new (std::size_t count, user-defined-args...); 
void* T::operator new[](std::size_t count, user-defined-args...); 

使用類特定的展示位置,新的運營商提供線索。

假設您編寫了一個分配多個數據成員的類,並且希望所有這些數據成員都位於同一個內存頁面上。如果數據分佈在多個內存頁面上,則CPU必須將不同的內存頁面加載到L1或L2緩存中,以便您可以訪問對象的成員數據。如果您的內存處理程序可以將所有對象的數據成員放在同一頁上,那麼您的程序將運行得更快,因爲CPU不需要將多個頁面加載到緩存中。

這些是特定於類的位置的新操作符。

void* T::operator new (std::size_t count, user-defined-args...); 
void* T::operator new[](std::size_t count, user-defined-args...); 
void* T::operator new (std::size_t count, 
    std::align_val_t al, user-defined-args...); // (since C++17) 
void* T::operator new[](std::size_t count, 
    std::align_val_t al, user-defined-args...); // (since C++17) 

通過提供提示參數使它們看起來像這樣。

void* T::operator new (std::size_t count, void* hint); 
void* T::operator new[](std::size_t count, void* hint); 
void* T::operator new (std::size_t count, std::align_val_t al, void* hint); // (since C++17) 
void* T::operator new[](std::size_t count, std::align_val_t al, void* hint); // (since C++17) 

的提示參數告訴內存處理程序嘗試放置對象沒有那一抹地址的位置,但在同一個頁面提示的地址上。

現在您可以編寫一個類似於您的內存處理類派生的類。

class Foo : public MemoryHandler 
{ 
public: 
    Foo(); 
    ... 
private: 
    Blah * b_; 
    Wham * f_; 
}; 

Foo::Foo() : b_(nullptr), f_(nullptr) 
{ 
    // This should put data members on the same memory page as this Foo object. 
    b_ = new (this) Blah; 
    f_ = new (this) Wham; 
} 
+0

謝謝。迄今爲止的最佳答案。非常詳細和可以理解。 – LincolnMan

2

您只需要將您使用的版本newdelete過載。根據[class.free]中的示例,在一個類中定義一個operator new函數將隱藏所有全局函數operator new函數。這與定義與基類函數具有相同名稱的方法或全局函數隱藏基本或全局版本的方法相同。

請注意,operator newoperator new[]是不同的名稱,所以重載operator new本身不會隱藏全局operator new[]函數。

2

如果我用new和delete運算符編寫C++類,是否需要重載所有這些?

不,你不需要重載所有它們。至少,您需要重載需要定製的操作員。

我想我們可以假設你在重載的操作符中做了一些特定的操作,否則你不需要它們。

問題變得更多我應該重載所有這些嗎?

是的,你可能應該。如果代碼根據代碼中使用的newdelete的形式做了完全不同的事情,這將是令人驚訝的,例如,

auto* obj1 = new Obj{}; 
// vs 
auto* obj2 = new Obj[5]; 

如果new運營商應用某些特殊的初始化,就有理由預計,這兩種形式會做初始化。

另一方面,如果其他形式不適用,那麼有利於完全刪除(= delete)這些重載。

The C++ operators進入「集合」,算術,流插入和提取,關係等等。通常的做法是,當集合中的一個運算符被重載時,其他運算符也是如此。

它並不總是適用,但通常是這樣。例如。拼接操作通常有operator+operator+=,但不是operator-operator-=

+0

贊同刪除它們,除了使它們私人。 – StoryTeller

+0

好點,我會更新。 – Niall

相關問題