operator delete, operator delete[]
定義於標頭檔案 <new> |
||
可替換的常規解除分配函式 |
||
(1) | ||
void operator delete ( void* ptr ) throw(); |
(C++11 前) | |
void operator delete ( void* ptr ) noexcept; |
(C++11 起) | |
(2) | ||
void operator delete[]( void* ptr ) throw(); |
(C++11 前) | |
void operator delete[]( void* ptr ) noexcept; |
(C++11 起) | |
void operator delete ( void* ptr, std::align_val_t al ) noexcept; |
(3) | (C++17 起) |
void operator delete[]( void* ptr, std::align_val_t al ) noexcept; |
(4) | (C++17 起) |
void operator delete ( void* ptr, std::size_t sz ) noexcept; |
(5) | (C++14 起) |
void operator delete[]( void* ptr, std::size_t sz ) noexcept; |
(6) | (C++14 起) |
void operator delete ( void* ptr, std::size_t sz, std::align_val_t al ) noexcept; |
(7) | (C++17 起) |
void operator delete[]( void* ptr, std::size_t sz, std::align_val_t al ) noexcept; |
(8) | (C++17 起) |
可替換的定位解除分配函式 |
||
(9) | ||
void operator delete ( void* ptr, const std::nothrow_t& tag ) throw(); |
(C++11 前) | |
void operator delete ( void* ptr, const std::nothrow_t& tag ) noexcept; |
(C++11 起) | |
(10) | ||
void operator delete[]( void* ptr, const std::nothrow_t& tag ) throw(); |
(C++11 前) | |
void operator delete[]( void* ptr, const std::nothrow_t& tag ) noexcept; |
(C++11 起) | |
void operator delete ( void* ptr, std::align_val_t al, const std::nothrow_t& tag ) noexcept; |
(11) | (C++17 起) |
void operator delete[]( void* ptr, std::align_val_t al, const std::nothrow_t& tag ) noexcept; |
(12) | (C++17 起) |
非分配定位解除分配函式 |
||
(13) | ||
void operator delete ( void* ptr, void* place ) throw(); |
(C++11 前) | |
void operator delete ( void* ptr, void* place ) noexcept; |
(C++11 起) | |
(14) | ||
void operator delete[]( void* ptr, void* place ) throw(); |
(C++11 前) | |
void operator delete[]( void* ptr, void* place ) noexcept; |
(C++11 起) | |
使用者定義的定位解除分配函式 |
||
void operator delete ( void* ptr, args... ); |
(15) | |
void operator delete[]( void* ptr, args... ); |
(16) | |
類特定的常規解除分配函式 |
||
void T::operator delete ( void* ptr ); |
(17) | |
void T::operator delete[]( void* ptr ); |
(18) | |
void T::operator delete ( void* ptr, std::align_val_t al ); |
(19) | (C++17 起) |
void T::operator delete[]( void* ptr, std::align_val_t al ); |
(20) | (C++17 起) |
void T::operator delete ( void* ptr, std::size_t sz ); |
(21) | |
void T::operator delete[]( void* ptr, std::size_t sz ); |
(22) | |
void T::operator delete ( void* ptr, std::size_t sz, std::align_val_t al ); |
(23) | (C++17 起) |
void T::operator delete[]( void* ptr, std::size_t sz, std::align_val_t al ); |
(24) | (C++17 起) |
類特定的定位解除分配函式 |
||
void T::operator delete ( void* ptr, args... ); |
(25) | |
void T::operator delete[]( void* ptr, args... ); |
(26) | |
類特定的常規銷燬解除分配函式 |
||
void T::operator delete( T* ptr, std::destroying_delete_t ); |
(27) | (C++20 起) |
void T::operator delete( T* ptr, std::destroying_delete_t, std::align_val_t al ); |
(28) | (C++20 起) |
void T::operator delete( T* ptr, std::destroying_delete_t, std::size_t sz ); |
(29) | (C++20 起) |
void T::operator delete( T* ptr, std::destroying_delete_t, std::size_t sz, std::align_val_t al ); |
(30) | (C++20 起) |
解除分配先前由匹配的 operator new 或 operator new[] 分配的儲存。這些解除分配函式由 delete 和 delete[] 表示式以及 定位 new 表示式 呼叫,以在銷燬(或未能構造)具有動態儲存期的物件後解除分配記憶體。它們也可以使用常規函式呼叫語法進行呼叫。
- 如果 ptr 不是空指標,並且滿足以下條件之一,則行為未定義:
- 對於 operator delete,ptr 的值不表示先前呼叫 (可能已替換的) operator new(std::size_t) (對於過載 (1,5,9)) 或 operator new(std::size_t, std::align_val_t) (對於過載 (3,7,11)) 所分配的記憶體塊的地址,並且該記憶體塊尚未因對 operator delete 的中間呼叫而失效。
- 對於 operator delete[],ptr 的值不表示先前呼叫 (可能已替換的) operator new[](std::size_t) (對於過載 (2,6,10)) 或 operator new[](std::size_t, std::align_val_t) (對於過載 (4,8,12)) 所分配的記憶體塊的地址,並且該記憶體塊尚未因對 operator delete[] 的中間呼叫而失效。
過載 (1-8) 在每個翻譯單元中隱式宣告,即使未包含 <new> 標頭檔案。
有關選擇過載的條件,請參閱 delete 表示式。
目錄 |
[編輯] 引數
ptr | - | 指向要解除分配的記憶體塊的指標或空指標 |
sz | - | 傳遞給匹配的分配函式的大小 |
place | - | 在匹配的定位 new 中用作定位引數的指標 |
tag | - | 過載消歧標記,與非丟擲 operator new 使用的標記匹配 |
al | - | 已分配物件或陣列元素的對齊方式 |
args | - | 匹配定位分配函式的任意引數(可能包括 std::size_t 和 std::align_val_t) |
[編輯] 異常
所有解除分配函式都是 noexcept(true),除非在宣告中另有規定。 |
(C++11 起) |
如果解除分配函式透過丟擲異常終止,則行為未定義,即使它被宣告為 noexcept(false)(C++11 起)。
[編輯] 全域性替換
全域性 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)
具有附加使用者定義引數(“定位形式”,(15,16))的 operator delete 和 operator delete[] 的過載可以在全域性作用域中宣告,並由 new 表示式的匹配定位形式呼叫,如果正在分配的物件的建構函式丟擲異常。
標準庫的 operator delete 和 operator delete[] 的定位形式 (13,14) 不能被替換,並且只能在定位 new 表示式未使用 ::new 語法的情況下進行自定義,方法是提供具有匹配簽名的類特定定位 delete (25,26):void T::operator delete(void*, void*) 或 void T::operator delete[](void*, void*)。
[編輯] 類特定過載
解除分配函式 (17-24) 可以定義為類的靜態成員函式。如果提供,這些解除分配函式由 delete 表示式在刪除此類物件 (17,19,21) 和陣列 (18,20,22) 時呼叫,除非 delete 表示式使用了 ::delete 形式,該形式會繞過類作用域查詢。static 關鍵字對於這些函式宣告是可選的:無論是否使用該關鍵字,解除分配函式始終是靜態成員函式。
delete 表示式從類作用域(陣列形式在陣列元素類的作用域中查詢)開始查詢適當的解除分配函式名稱,如果沒有找到成員,則像往常一樣繼續到全域性作用域。請注意,根據名稱查詢規則,在類作用域中宣告的任何解除分配函式都會隱藏所有全域性解除分配函式。
如果正在刪除的物件的靜態型別與其動態型別不同(例如透過指向基類的指標刪除多型物件時),並且靜態型別中的解構函式是虛擬函式,則單個物件形式的 delete 會從其虛解構函式最終重寫器的定義點開始查詢解除分配函式的名稱。無論執行時將執行哪個解除分配函式,operator delete 的靜態可見版本必須可訪問才能編譯。在其他情況下,當透過指向基類的指標刪除陣列時,或透過指向具有非虛解構函式的基類的指標刪除時,行為未定義。
如果未提供單引數過載 (17,18),但提供了接受 std::size_t 作為第二個引數的大小感知過載 (21,22),則會為正常解除分配呼叫大小感知形式,並且 C++ 執行時將要解除分配物件的大小作為第二個引數傳遞。如果兩種形式都已定義,則呼叫不感知大小的版本。
#include <cstddef> #include <iostream> // sized class-specific deallocation functions struct X { static void operator delete(void* ptr, std::size_t sz) { std::cout << "custom delete for size " << sz << '\n'; ::operator delete(ptr); } static void operator delete[](void* ptr, std::size_t sz) { std::cout << "custom delete for size " << sz << '\n'; ::operator delete[](ptr); } }; int main() { X* p1 = new X; delete p1; X* p2 = new X[10]; delete[] p2; }
可能的輸出
custom delete for size 1 custom delete for size 18
具有附加使用者定義引數(“定位形式”,(25,26))的 operator delete 和 operator delete[] 的過載也可以定義為類成員。當失敗的定位 new 表示式查詢要呼叫的相應定位 delete 函式時,它會在檢查全域性作用域之前從類作用域開始查詢,並查詢與定位 new 簽名匹配的函式。
#include <cstddef> #include <iostream> #include <stdexcept> struct X { X() { throw std::runtime_error("X(): std::runtime_error"); } // custom placement new static void* operator new(std::size_t sz, bool b) { std::cout << "custom placement new called, b = " << b << '\n'; return ::operator new(sz); } // 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& ex) { std::cout << ex.what() << '\n'; } }
輸出
custom placement new called, b = 1 custom placement delete called, b = 1 X(): std::runtime_error
如果類級別的 operator delete 是模板函式,則它必須具有 void 返回型別,第一個引數為 void*,並且它必須有兩個或更多引數。換句話說,只有定位形式可以是模板。無論其簽名如何,模板例項都不是常規解除分配函式。模板 operator delete 的特化透過模板引數推導選擇。
[編輯] 注意
在多型類上呼叫類特定的 T::operator delete 是靜態成員函式透過動態排程呼叫的唯一情況。
以下函式必須是執行緒安全的
對這些函式中分配或解除分配特定儲存單元的呼叫以單一總順序發生,並且每次解除分配呼叫都先於此順序中的下一次分配(如果有)。 |
(C++11 起) |
特性測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_sized_deallocation |
201309L |
(C++14) | 尺寸感知解除分配 |
__cpp_impl_destroying_delete |
201806L |
(C++20) | 銷燬式 operator delete(編譯器支援) |
__cpp_lib_destroying_delete |
201806L |
(C++20) | 銷燬式 operator delete(庫支援) |
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 220 | C++98 | 允許使用者定義的解除分配函式丟擲異常 | 從解除分配函式丟擲異常 導致未定義行為 |
CWG 1438 | C++98 | 任何使用無效指標值的行為都未定義 | 只有間接引用和解除分配是 |
LWG 206 | C++98 | 替換 (2) 不影響 (10) 的預設行為 | 預設行為 相應改變 |
LWG 298 | C++98 | 替換 (1) 不影響 (9) 的預設行為 | 預設行為 相應改變 |
LWG 404 | C++98 | 可替換解除分配函式的替換 函式可以宣告為 inline |
禁止,無需診斷 |
LWG 2458 | C++14 | 指定了接受 (void*, std::size_t, const std::nothrow_t&) 的過載,但從未被呼叫 |
移除了多餘的過載 |
[編輯] 另請參閱
[static] (C++23) |
解除分配先前從 operator new 獲取的記憶體( std::generator<Ref,V,Allocator>::promise_type 的公共靜態成員函式) |
分配函式 (函式) | |
(在 C++17 中已棄用)(在 C++20 中已移除) |
釋放未初始化儲存 (函式模板) |
釋放先前分配的記憶體 (函式) |