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 expression 。
目录 |
参数
| 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 ) 是 可替换的 。默认版本的效果如下:
全局
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 中移除)
|
释放未初始化的存储空间
(函数模板) |
|
释放先前分配的内存
(函数) |