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 分配的存储空间。这些释放函数由 delete 表达式 和 new 表达式 调用,用于在销毁(或无法构造)具有动态存储期的对象后释放内存。它们也可以使用常规的函数调用语法进行调用。
operator delete
之前对 *p 执行析构函数。相反,由用户定义的 operator delete 负责直接调用析构函数,例如通过 p->~T();。
有关通常(非位置)释放函数的对齐感知和对齐不感知重载之间的重载解析规则的详细信息,请参阅 delete 表达式。 |
(自 C++17 起) |
在所有情况下,如果 ptr 是空指针,则标准库释放函数不会执行任何操作。如果传递给标准库释放函数的指针不是从相应的标准库分配函数获取的,则行为未定义。
标准库释放函数返回后,所有引用已释放存储区的任何部分的指针都将失效。
通过以这种方式失效的指针进行间接访问,并将其传递给释放函数(双重释放)是未定义的行为。任何其他使用都是实现定义的行为。
内容 |
[编辑] 参数
ptr | - | 指向要释放的内存块的指针或空指针 |
sz | - | 传递给匹配分配函数的大小 |
place | - | 在匹配的带位置的 new 中用作位置参数的指针 |
tag | - | 与非抛出 operator new 使用的标签匹配的重载消歧标签 |
al | - | 已分配的对象或数组元素的对齐方式 |
args | - | 与带位置的分配函数匹配的任意参数(可能包括 std::size_t 和 std::align_val_t) |
[编辑] 返回值
(无)
[编辑] 异常
所有释放函数都是 noexcept(true),除非声明中另有说明。 |
(自 C++11 起) |
如果释放函数通过抛出异常终止,则行为未定义,即使它用 noexcept(false) 声明(自 C++11 起).
[编辑] 全局替换
可替换的释放函数 (1-12) 在每个翻译单元中隐式声明,即使没有包含 <new> 头文件。这些函数是可替换的:用户提供的具有相同签名的非成员函数在程序中的任何位置(在任何源文件中)定义,将替换整个程序的相应隐式版本。它的声明不需要可见。
如果程序中提供了多个替换,或者如果替换用 inline
说明符 声明,则程序格式不正确,但不需要诊断。如果在全局命名空间以外的命名空间中定义了替换,或者如果将其定义为全局范围内的静态非成员函数,则程序格式不正确。
标准库实现的无抛出版本 (9,10) 直接调用相应的抛出版本 (1,2)。标准库实现的大小感知释放函数 (5-8) 直接调用相应的大小不感知释放函数 (1-4)。标准库实现的大小不感知抛出数组形式 (2,4) 直接调用相应的单个对象形式 (1,3)。因此,替换抛出单个对象的释放函数 (1,3) 足以处理所有释放。
全局 operator
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
(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*,并且它必须具有两个或更多个参数。换句话说,只有放置形式可以是模板。模板实例永远不会是普通的释放函数,无论其签名如何。模板运算符删除的专门化是使用模板参数推导选择的。
[编辑] 备注
在多态类上调用类特定的T::operator delete是唯一一个通过动态调度调用静态成员函数的情况。
如果释放函数的行为不满足默认约束,则行为未定义。
以下函数需要线程安全
对这些函数的调用,这些函数分配或释放特定的存储单元,以单一总顺序发生,并且每个这样的释放调用发生在此顺序中的下一个分配(如果有)之前。 |
(自 C++11 起) |
特性测试宏 | 值 | Std | 功能 |
---|---|---|---|
__cpp_sized_deallocation |
201309L | (C++14) | 大小释放 |
__cpp_impl_destroying_delete |
201806L | (C++20) | 销毁运算符删除(编译器支持) |
__cpp_lib_destroying_delete |
201806L | (C++20) | 销毁运算符删除(库支持) |
[编辑] 缺陷报告
以下行为更改的缺陷报告被追溯应用于先前发布的 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 中删除) |
释放未初始化的存储空间 (函数模板) |
释放之前分配的内存 (函数) |