名稱空間
變體
操作

operator delete, operator delete[]

來自 cppreference.com
< cpp‎ | 記憶體‎ | new
 
 
 
記憶體管理庫
(僅作說明*)
未初始化記憶體演算法
(C++17)
(C++17)
(C++17)
受約束的未初始化
記憶體演算法
C 庫

分配器
記憶體資源
垃圾回收支援
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
未初始化儲存
(直到 C++20*)
(直到 C++20*)
顯式生命週期管理
 
 
定義於標頭檔案 <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 newoperator new[] 分配的儲存。這些解除分配函式由 deletedelete[] 表示式以及 定位 new 表示式 呼叫,以在銷燬(或未能構造)具有動態儲存期的物件後解除分配記憶體。它們也可以使用常規函式呼叫語法進行呼叫。

1-12) 可替換的解除分配函式。標準庫為這些函式提供了預設實現,預設實現的效果請參閱下方
1-8)deletedelete[] 表示式呼叫。使任何非空 ptr 失效。
9-12)初始化失敗時由定位 new 表示式呼叫。operator delete[] 使任何非空 ptr 失效。
如果 ptr 不是空指標,並且滿足以下條件之一,則行為未定義:
  • 對於 operator deleteptr 的值不表示先前呼叫 (可能已替換的) 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[] 的中間呼叫而失效。
13,14) 當表示式中的任何初始化部分因丟擲異常而終止時,由呼叫 非分配定位分配函式 的定位 new 表示式呼叫。不執行任何操作。
15-30)deletedelete[] 和定位 new 表示式呼叫的使用者定義解除分配函式。
27-30) 如果定義,delete 表示式在呼叫 operator delete 之前不會執行 *ptr 的解構函式。相反,解構函式的直接呼叫,例如透過 ptr->~T();,成為此 operator delete 的責任。

過載 (1-8) 在每個翻譯單元中隱式宣告,即使未包含 <new> 標頭檔案。

有關選擇過載的條件,請參閱 delete 表示式

目錄

[編輯] 引數

ptr - 指向要解除分配的記憶體塊的指標或空指標
sz - 傳遞給匹配的分配函式的大小
place - 在匹配的定位 new 中用作定位引數的指標
tag - 過載消歧標記,與非丟擲 operator new 使用的標記匹配
al - 已分配物件或陣列元素的對齊方式
args - 匹配定位分配函式的任意引數(可能包括 std::size_tstd::align_val_t

[編輯] 異常

所有解除分配函式都是 noexcept(true),除非在宣告中另有規定。

(C++11 起)

如果解除分配函式透過丟擲異常終止,則行為未定義,即使它被宣告為 noexcept(false)(C++11 起)

[編輯] 全域性替換

過載 (1-12)可替換的。預設版本的效果是:

1) 如果 ptr 為空,則不執行任何操作。否則,回收先前呼叫 operator new 分配的儲存。
2) 呼叫 operator delete(ptr),如同過載 (1) 可以回收先前呼叫 operator new[] 分配的儲存。
3)(1)
4) 呼叫 operator delete(ptr, al),如同過載 (3) 可以回收先前呼叫 operator new[] 分配的儲存。
5) 呼叫 operator delete(ptr)
6) 呼叫 operator delete[](ptr)
7) 呼叫 operator delete(ptr, al)
8) 呼叫 operator delete[](ptr, al)
9) 呼叫 operator delete(ptr)
10) 呼叫 operator delete[](ptr)
11) 呼叫 operator delete(ptr, al)
12) 呼叫 operator delete[](ptr, al)

全域性 operators 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 deleteoperator delete[] 的過載可以在全域性作用域中宣告,並由 new 表示式的匹配定位形式呼叫,如果正在分配的物件的建構函式丟擲異常。

標準庫的 operator deleteoperator 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 deleteoperator 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 中已移除)
釋放未初始化儲存
(函式模板) [編輯]
釋放先前分配的記憶體
(函式) [編輯]