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[] 无效化。
即使未包含 <new> 头文件,重载 (1-8) 也会在每个翻译单元中隐式声明。
有关选择重载的标准,请参见 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) 可以定义为类的静态成员函数。如果提供了这些释放函数,则在删除此类的对象 (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> // 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
带有附加用户定义参数(“ 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"); } // 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*,并且必须具有两个或多个参数。 换句话说,只有 placement 形式可以是模板。 模板实例永远不是通常的释放函数,无论其签名如何。 模板 operator delete 的特化是使用 模板参数推导 选择的。
[编辑] 注释
在多态类上调用类特定的 T::operator delete 是通过动态调度调用静态成员函数的唯一情况。
以下函数需要是线程安全的
对这些函数的调用(这些函数分配或释放特定的存储单元)以单一的总顺序发生,并且每个这样的释放调用 先于 在此顺序中的下一个分配(如果有)。 |
(自 C++11 起) |
Feature-test macro | 值 | Std | 特性 |
---|---|---|---|
__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++ 标准。
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&) 已指定,但永远无法调用 |
移除了虚假的重载 |
[编辑] 参见
[静态] (C++23) |
释放先前从 operator new 获取的内存( std::generator<Ref,V,Allocator>::promise_type 的公共静态成员函数) |
分配函数 (函数) | |
(在 C++17 中已弃用)(在 C++20 中已移除) |
释放未初始化的存储 (函数模板) |
释放先前分配的内存 (函数) |