operator new, operator new[]
定義於標頭檔案 <new> |
||
可替換的分配函式 |
||
void* operator new ( std::size_t count ); |
(1) | |
void* operator new[]( std::size_t count ); |
(2) | |
void* operator new ( std::size_t count, std::align_val_t al ); |
(3) | (C++17 起) |
void* operator new[]( std::size_t count, std::align_val_t al ); |
(4) | (C++17 起) |
可替換的非丟擲分配函式 |
||
void* operator new ( std::size_t count, const std::nothrow_t& tag ); |
(5) | (C++11 起無異常丟擲) |
void* operator new[]( std::size_t count, const std::nothrow_t& tag ); |
(6) | (C++11 起無異常丟擲) |
void* operator new ( std::size_t count, std::align_val_t al, const std::nothrow_t& tag ) noexcept; |
(7) | (C++17 起) |
void* operator new[]( std::size_t count, std::align_val_t al, const std::nothrow_t& tag ) noexcept; |
(8) | (C++17 起) |
不分配的就地分配函式 |
||
void* operator new ( std::size_t count, void* ptr ); |
(9) | (C++11 起無異常丟擲) (C++26 起為 constexpr) |
void* operator new[]( std::size_t count, void* ptr ); |
(10) | (C++11 起無異常丟擲) (C++26 起為 constexpr) |
使用者定義的就地分配函式 |
||
void* operator new ( std::size_t count, /* args... */ ); |
(11) | |
void* operator new[]( std::size_t count, /* args... */ ); |
(12) | |
void* operator new ( std::size_t count, std::align_val_t al, /* args... */ ); |
(13) | (C++17 起) |
void* operator new[]( std::size_t count, std::align_val_t al, /* args... */ ); |
(14) | (C++17 起) |
類特有的分配函式 |
||
void* T::operator new ( std::size_t count ); |
(15) | |
void* T::operator new[]( std::size_t count ); |
(16) | |
void* T::operator new ( std::size_t count, std::align_val_t al ); |
(17) | (C++17 起) |
void* T::operator new[]( std::size_t count, std::align_val_t al ); |
(18) | (C++17 起) |
類特有的就地分配函式 |
||
void* T::operator new ( std::size_t count, /* args... */ ); |
(19) | |
void* T::operator new[]( std::size_t count, /* args... */ ); |
(20) | |
void* T::operator new ( std::size_t count, std::align_val_t al, /* args... */ ); |
(21) | (C++17 起) |
void* T::operator new[]( std::size_t count, std::align_val_t al, /* args... */ ); |
(22) | (C++17 起) |
嘗試分配請求的位元組數,分配請求可能會失敗(即使請求的位元組數為零)。這些分配函式由 new 表示式 呼叫,以分配記憶體,然後在其中初始化新物件。它們也可以使用常規函式呼叫語法呼叫。
即使不包含 <new> 標頭檔案,在每個翻譯單元中也會隱式宣告過載 (1-4)。
有關選擇過載的標準,請參見 new 表示式。
目錄 |
[編輯] 引數
count | - | 要分配的位元組數 |
ptr | - | 指向要初始化物件的記憶體區域的指標 |
tag | - | 用於選擇非丟擲過載的歧義消除標籤 |
al | - | 要使用的對齊方式,無效值會導致未定義行為 |
[編輯] 返回值
[編輯] 異常
[編輯] 全域性替換
- 如果嘗試成功,則返回指向已分配儲存的指標。
- 否則,如果當前未安裝新處理程式,則丟擲 std::bad_alloc。
- 否則,呼叫當前安裝的新處理程式。
- 如果新處理程式返回,則開始另一次分配嘗試。
- 否則,退出當前呼叫。
- 如果呼叫正常返回,則返回該呼叫的結果。
- 否則,返回空指標。
在獨立實現上,(1-8) 的預設版本是否滿足上述要求是實現定義的。建議獨立實現,如果這些預設版本中的任何一個滿足託管實現的要求,那麼所有版本都應該滿足。 |
(C++26 起) |
全域性 operator
s new/delete 替換
#include <cstdio> #include <cstdlib> #include <new> // no inline, required by [replacement.functions]/3 void* operator new(std::size_t sz) { std::printf("1) new(size_t), size = %zu\n", sz); if (sz == 0) ++sz; // avoid std::malloc(0) which may return nullptr on success if (void *ptr = std::malloc(sz)) return ptr; throw std::bad_alloc{}; // required by [new.delete.single]/3 } // no inline, required by [replacement.functions]/3 void* operator new[](std::size_t sz) { std::printf("2) new[](size_t), size = %zu\n", sz); if (sz == 0) ++sz; // avoid std::malloc(0) which may return nullptr on success if (void *ptr = std::malloc(sz)) return ptr; throw std::bad_alloc{}; // required by [new.delete.single]/3 } void operator delete(void* ptr) noexcept { std::puts("3) delete(void*)"); std::free(ptr); } void operator delete(void* ptr, std::size_t size) noexcept { std::printf("4) delete(void*, size_t), size = %zu\n", size); std::free(ptr); } void operator delete[](void* ptr) noexcept { std::puts("5) delete[](void* ptr)"); std::free(ptr); } void operator delete[](void* ptr, std::size_t size) noexcept { std::printf("6) delete[](void*, size_t), size = %zu\n", size); std::free(ptr); } int main() { int* p1 = new int; delete p1; int* p2 = new int[10]; // guaranteed to call the replacement in C++11 delete[] p2; }
可能的輸出
// Compiled with GCC-5 in C++17 mode to obtain the following: 1) op new(size_t), size = 4 4) op delete(void*, size_t), size = 4 2) op new[](size_t), size = 40 5) op delete[](void* ptr)
帶有額外使用者定義引數的 operator new
和 operator new[]
過載(“就地形式”,版本 (11-14))可以像往常一樣在全域性作用域宣告,並由匹配的 new 表示式的就地形式呼叫。
標準庫的非分配就地形式的 operator new
(9,10) 不能被替換,並且只能透過提供具有匹配簽名的類特有就地 new (19,20) 來定製,如果就地 new 表示式沒有使用 ::new 語法:void* T::operator new(std::size_t, void*) 或 void* T::operator new[](std::size_t, void*)。
就地形式 void* operator new(std::size_t, std::size_t) 不允許,因為解除分配函式的匹配簽名 void operator delete(void*, std::size_t) 是一個常用(非就地)的解除分配函式。 |
(C++14 起) |
[編輯] 類特有過載
單物件和陣列分配函式都可以定義為類的公共靜態成員函式(版本 (15-18))。如果定義,這些分配函式將由 new 表示式呼叫,以分配此類的單個物件和陣列的記憶體,除非 new 表示式使用了 ::new 形式,它繞過了類作用域查詢。關鍵字 static 對於這些函式是可選的:無論是否使用,分配函式都是靜態成員函式。
new 表示式首先在類作用域中查詢適當的分配函式名稱,然後在全域性作用域中查詢。請注意,根據名稱查詢規則,在類作用域中宣告的任何分配函式都會對嘗試分配此類的物件的 new 表示式隱藏所有全域性分配函式。
當分配其對齊方式超過 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的物件和物件陣列時,過載決議執行兩次:首先針對對齊感知函式簽名,然後針對對齊無關函式簽名。這意味著,如果一個具有擴充套件對齊的類有一個對齊無關的類特有分配函式,則將呼叫該函式,而不是全域性對齊感知分配函式。這是故意的:類成員被期望最瞭解如何處理該類。 |
(C++17 起) |
當分配其對齊方式不超過 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的物件和物件陣列時,過載決議執行兩次:首先針對對齊無關函式簽名,然後針對對齊感知函式簽名。 |
(C++20 起) |
#include <cstddef> #include <iostream> // class-specific allocation functions struct X { static void* operator new(std::size_t count) { std::cout << "custom new for size " << count << '\n'; return ::operator new(count); } static void* operator new[](std::size_t count) { std::cout << "custom new[] for size " << count << '\n'; return ::operator new[](count); } }; int main() { X* p1 = new X; delete p1; X* p2 = new X[10]; delete[] p2; }
可能的輸出
custom new for size 1 custom new[] for size 10
帶有額外使用者定義引數的 operator new
和 operator new[]
過載(“就地形式”)也可以定義為類成員 (19-22))。當具有匹配簽名的就地 new 表示式查詢要呼叫的相應分配函式時,它首先在類作用域中查詢,然後才檢查全域性作用域,如果提供了類特有就地 new,則會呼叫它。
當分配其對齊方式超過 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的物件和物件陣列時,就地形式的過載決議執行兩次,就像常規形式一樣:首先針對對齊感知函式簽名,然後針對對齊無關函式簽名。 |
(C++17 起) |
當分配其對齊方式不超過 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的物件和物件陣列時,就地形式的過載決議執行兩次,就像常規形式一樣:首先針對對齊無關函式簽名,然後針對對齊感知函式簽名。 |
(C++20 起) |
#include <cstddef> #include <iostream> #include <stdexcept> struct X { X() { throw std::runtime_error(""); } // custom placement new static void* operator new(std::size_t count, bool b) { std::cout << "custom placement new called, b = " << b << '\n'; return ::operator new(count); } // custom placement delete static void operator delete(void* ptr, bool b) { std::cout << "custom placement delete called, b = " << b << '\n'; ::operator delete(ptr); } }; int main() { try { [[maybe_unused]] X* p1 = new (true) X; } catch (const std::exception&) {} }
輸出
custom placement new called, b = 1 custom placement delete called, b = 1
如果類級別的 operator new
是模板函式,則其返回型別必須為 void*,第一個引數為 std::size_t,並且必須具有兩個或更多引數。換句話說,只有就地形式可以是模板。
[編輯] 注意
儘管非分配就地 new (9,10) 不能被替換,但可以在類作用域中定義具有相同簽名的函式,如上所述。此外,允許全域性過載看起來像就地 new 但將非 void 指標型別作為第二個引數,因此希望確保呼叫真正的就地 new 的程式碼(例如 std::allocator::construct)必須使用 ::new 並將指標強制轉換為 void*。
如果解除分配函式的行為不滿足預設約束,則行為未定義。
以下函式必須是執行緒安全的
這些函式分配或解除分配特定儲存單元的呼叫以單一總順序發生,並且每次此類解除分配呼叫都先於此順序中的下一次分配(如果有)。 |
(C++11 起) |
未指定庫版本的 operator new
是否呼叫 std::malloc 或 std::aligned_alloc(C++17 起)。
對於載入大型檔案,透過作業系統特定函式進行檔案對映,例如 POSIX 上的 mmap
或 Windows 上的 CreateFileMapping
(A
/W
) 以及 MapViewOfFile
,優於為檔案讀取分配緩衝區。
特性測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_lib_freestanding_operator_new |
202306L |
(C++26) | 可替換的 operator new 的獨立支援[1] |
0 |
(C++26) | 無獨立支援 | |
__cpp_lib_constexpr_new |
202406L |
(C++26) | constexpr 就地 new 和 new[] |
- ↑ 形式上,如果可替換的全域性分配函式的所有預設版本都滿足託管實現的要求,則此宏展開為 202306L。
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 521 | C++98 | 任何派生自 std::bad_alloc 的類都可能被丟擲, 即使 std::bad_alloc 基類是模糊的或不可訪問的 |
丟擲的異常應該匹配 std::bad_alloc 型別的處理程式 |
LWG 9 | C++98 | 多次呼叫分配零 位元組可能會得到相同的指標 |
只有在所有先前 生成的指標都已 傳遞給解除分配函式 |
LWG 206 | C++98 | 替換可替換的分配函式 不影響相應可替換非丟擲分配函式的預設行為 相應地改變 |
預設行為 改變 |
LWG 404 | C++98 | 可替換分配函式的替換 函式可以宣告為 inline |
被禁止,不需要診斷 |
[編輯] 參考資料
- C++23 標準 (ISO/IEC 14882:2024)
- 17.7 動態記憶體管理 [support.dynamic]
- C++20 標準 (ISO/IEC 14882:2020)
- 17.6 動態記憶體管理 [support.dynamic]
- C++17 標準 (ISO/IEC 14882:2017)
- 21.6 動態記憶體管理 [support.dynamic]
- C++14 標準 (ISO/IEC 14882:2014)
- 18.6 動態記憶體管理 [support.dynamic]
- C++11 標準 (ISO/IEC 14882:2011)
- 18.6 動態記憶體管理 [support.dynamic]
- C++03 標準 (ISO/IEC 14882:2003)
- 18.4 動態記憶體管理 [lib.support.dynamic]
- C++98 標準 (ISO/IEC 14882:1998)
- 18.4 動態記憶體管理 [lib.support.dynamic]
[編輯] 另請參見
[static] (C++23) |
使用 Allocator 分配記憶體( std::generator<Ref,V,Allocator>::promise_type 的公共靜態成員函式) |
釋放函式 (函式) | |
(C++11) |
獲取當前的 new 處理函式 (函式) |
註冊一個 new 處理函式 (函式) | |
(在 C++17 中已棄用)(在 C++20 中已移除) |
獲取未初始化儲存 (函式模板) |
分配記憶體 (函式) | |
(C++17) |
分配對齊記憶體 (函式) |