Namespaces
Variants

operator delete、operator delete[]

来自 zh.cppreference.net
< cpp | 内存 | new
 
 
 
内存管理库
分配器
内存资源
未初始化存储 (直到 C++20*)
垃圾回收器支持 (直到 C++23)
 
 
定义于头文件 <new>
可替换的常规销毁函数
void operator delete  ( void* ptr ) throw();
(1) (直到 C++11)
void operator delete  ( void* ptr ) noexcept;
(自 C++11)
void operator delete[]( void* ptr ) throw();
(2) (直到 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)
可替换的放置销毁函数
void operator delete  ( void* ptr, const std::nothrow_t& tag ) throw();
(9) (直到 C++11)
void operator delete  ( void* ptr, const std::nothrow_t& tag ) noexcept;
(自 C++11)
void operator delete[]( void* ptr, const std::nothrow_t& tag ) throw();
(10) (直到 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)
非分配放置销毁函数
void operator delete  ( void* ptr, void* place ) throw();
(13) (直到 C++11)
void operator delete  ( void* ptr, void* place ) noexcept;
(自 C++11)
void operator delete[]( void* ptr, void* place ) throw();
(14) (直到 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)
类特定的常规 destroying 销毁函数
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表达式调用,用于在销毁(或构造失败)具有动态存储期的对象后归还内存。也可以通过常规函数调用语法调用它们。

1-12) 可替换销毁函数。标准库为这些函数提供默认实现,关于默认实现的效果,参见下文。
1-8)由 delete 和 delete[] 表达式调用。使任何非空 ptr 失效。
9-12)由放置 new 表达式在初始化失败时调用。operator delete[]使任何非空 ptr 失效。
如果 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[] 调用所失效。
13,14)由调用了new非分配放置分配函数的放置 表达式在表达式中任何部分初始化因抛出异常而终止时调用。不执行任何操作。
15-30)用户定义的销毁函数,由 delete、delete[] 和放置 new 表达式调用。
27-30)如果定义了这些函数,delete表达式在调用 *ptr 之前不会为 operator delete 执行析构函数。相反,直接调用析构函数(例如通过 ptr->~T();)的责任落在此 operator delete 上。

重载 (1-8)在每个翻译单元中隐式声明,即使未包含 <new> 头文件。

有关选择重载的标准,请参见 delete表达式。

参数

ptr - 指向要释放的内存块的指针或空指针
sz - 传递给匹配分配函数的大小值
place - 在匹配的placement new中作为放置参数使用的指针
tag - 与无抛出operator new所用标签匹配的重载消歧标签
al - 已分配对象或数组元素的对齐要求
args - 匹配placement分配函数的任意参数(可能包含 std::size_t 和 std::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 ) 。

全局 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)

具有额外用户定义参数("placement 形式", ( 15,16 ) )的 operator delete 和 operator delete [ ] 重载可以像通常那样在全局作用域中声明,当正在分配对象的构造函数抛出异常时,会被匹配的 new 表达式的 placement 形式调用。

标准库的 placement 形式 operator delete 和 operator delete [ ] ( 13,14 ) 不可被替换,且仅当 placement new 表达式未使用 :: new 语法时,才能通过提供具有匹配签名的类特定 placement delete ( 25,26 ) 进行自定义: void T :: operator delete ( void * , void * ) 或 void T :: operator delete [ ] ( void * , void * ) 。

类特定重载

释放函数 ( 17-24 ) 可定义为类的静态成员函数。当删除该类对象 ( 17,19,21 ) 和数组 ( 18,20,22 ) 时,若存在这些释放函数,则会被 delete 表达式调用,除非该delete表达式使用了 :: delete 形式(此形式会绕过类作用域查找)。这些函数声明中 static 关键字是可选的:无论是否使用该关键字,释放函数始终是静态成员函数。

delete表达式从类作用域开始查找适当的解分配函数名称(数组形式在数组元素类的作用域中查找),如果未找到成员函数则按常规方式继续在全局作用域查找。请注意,根据 名称查找规则 ,在类作用域中声明的任何解分配函数都会隐藏所有全局解分配函数。

如果被删除对象的静态类型与其动态类型不同(例如通过基类指针删除 多态 对象),且静态类型中的析构函数为虚函数,则delete的单对象形式会从最终覆盖其虚析构函数的定义点开始查找解分配函数的名称。无论运行时实际执行哪个解分配函数,静态可见版本的 operator delete 必须可访问才能通过编译。其他情况下,当通过基类指针删除数组,或通过基类指针删除具有非虚析构函数的对象时,其行为是未定义的。

如果未提供单参数重载 ( 17,18 ) ,但提供了接受 std::size_t 作为第二个参数的感知大小重载 ( 21,22 ) ,则普通释放操作会调用感知大小形式,C++运行时会将被释放对象的大小作为第二个参数传递。如果同时定义两种形式,则调用不感知大小的版本。

#include <cstddef>
#include <iostream>
// 带大小的类特定释放函数
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

具有额外用户定义参数("placement 形式", ( 25,26 ) )的 operator delete 和 operator delete [ ] 重载版本也可定义为类的成员函数。当失败的 placement new 表达式查找要调用的对应 placement delete 函数时,会在检查全局作用域之前先从类作用域开始查找,并寻找与 placement new 签名匹配的函数:

#include <cstddef>
#include <iostream>
#include <stdexcept>
struct X
{
    X() { throw std::runtime_error("X(): std::runtime_error"); }
    // 自定义 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);
    }
    // 自定义 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 * ,且必须包含两个或更多参数。换言之,只有placement形式才能作为模板。无论其签名如何,模板实例永远不会是常规的释放函数。模板operator delete的特化通过 模板实参推导 进行选择。

注释

对多态类调用类特定的 T :: operator delete 是通过动态分派调用静态成员函数的唯一情况。

下列函数必须满足线程安全要求:

针对特定存储单元的分配或释放函数调用会按单一全序执行,且每次释放调用 先于 该顺序中的下一次分配调用(若存在)。

(C++11 起)
功能测试 宏 值 标准 功能
__cpp_sized_deallocation 201309L (C++14) 带尺寸的释放操作
__cpp_impl_destroying_delete 201806L (C++20) 销毁式 delete 运算符(编译器支持)
__cpp_lib_destroying_delete 201806L (C++20) 销毁式 delete 运算符(库支持)

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 适用版本 发布行为 正确行为
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 中移除)
释放未初始化的存储空间
(函数模板)
释放先前分配的内存
(函数)