系統程式設計師的 C++

placement new(定位 new,在既有記憶體上建構物件)

通常,new 一次做兩件事:它從堆積配置記憶體,然後在其中建構一個物件。但有時你已經擁有記憶體——一個你自己配置的緩衝區、記憶體池裡的一個槽、一塊共享記憶體——而你只想要第二件事,也就是就地完成建構。placement new 是跳過配置、改在你交給它的位址上建構物件的那種 new 形式。

語法是 new (address) Type(args...):你在括號裡傳入一個指向已取得、大小與對齊都合適之儲存空間的指標,new 就恰好在那個位置建構一個 Type 物件,執行它的建構子卻不配置任何東西。反面是,因為 placement new 沒有配置,你絕不可 delete 這個物件——沒有相符的釋放可與它配對。取而代之,你在用完時明確呼叫解構子(p->~Type()),並另外以你當初取得緩衝區的方式釋放底層緩衝區。這個手動的兩步——明確建構、明確解構——正是把物件生存期與記憶體配置解耦的代價。

為何重要:placement new 是容器與配置器實際運作方式的基礎。舉例來說,std::vector 預先配置一塊裸緩衝區,然後在你 push_back 時用 placement new 把每個元素建構進去,這正是它能在不建構尚未持有之物件的情況下預留容量的方法。它對記憶體池、std::optional、std::variant 與序列化也不可或缺。誠實的危險很尖銳:儲存空間必須為該型別正確對齊(對齊錯誤是未定義行為),你要完全負責恰好呼叫一次解構子,忘記它就會洩漏物件所擁有的任何資源——即便裸記憶體本身被釋放了。這是低階、易錯的機制——大多數程式碼應該讓標準容器替你做。

alignas(Widget) std::byte buf[sizeof(Widget)]; Widget* w = new (buf) Widget(args); /* 使用 w */ w->~Widget(); // 明確解構;不 delete

物件被建構在一個既有、正確對齊的緩衝區裡;你必須親手呼叫解構子,且絕不可 delete 它。

placement new 不配置任何東西,所以沒有相符的 delete:把它與 delete 配對是未定義行為。緩衝區也必須滿足型別的對齊需求——在對齊不足的位元組裡建構 Widget 是未定義行為,標準對它不施加任何要求,所以最佳化器可以假設它從不發生。

又稱
construct-in-placein-place construction定位 new