低层内存管理
new 表达式是创建拥有动态存储期对象或对象数组的仅有方式,即它们拥有不受制于创建所它们在的作用域的生存期。 new 表达式通过调用分配函数获得存储。 delete 表达式销毁最终导出对象或通过 new 表达式创造的数组,然后调用解分配函数。默认分配函数和默认解分配函数,及与之关联的函数、类型及对象声明于头文件 <new> 。
解分配函数
operator delete,
operator delete[]
定义于头文件 | ||
可替换通常解分配函数 | ||
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, | (7) | (C++17 起) |
void operator delete[]( void* ptr, std::size_t sz, | (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, | (11) | (C++17 起) |
void operator delete[]( void* ptr, std::align_val_t al, | (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) | |
类特定销毁解分配函数 | ||
void T::operator delete(T* ptr, std::destroying_delete_t); | (27) | (C++20 起) |
void T::operator delete(T* ptr, std::destroying_delete_t, | (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, | (30) | (C++20 起) |
解分配先前由匹配的 operator new 所分配的存储。这些解分配函数为 delete 表达式与 new 表达式所调用,以在析构(或构造失败)拥有动态存储期的对象后解分配内存。它们亦可用常规函数调用语法调用。
1) 为 delete 表达式所调用,以解分配先前为单对象分配的存储。此函数的标准库实现的行为未定义,除非 ptr
是空指针或是先前从 operator new(size_t) 或 operator new(size_t, std::nothrow_t) 的标准库实现获得的指针。
2) 为 delete[] 表达式所调用,以解分配先前为对象数组分配的存储。此函数的标准库实现的行为未定义,除非 ptr
是空指针或是先前从 operator new[](size_t) 或 operator new[](size_t, std::nothrow_t) 的标准库实现获得的指针。
3,4) 同 (1,2) ,除了若对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__
才被调用
5,6) 若提供用户定义重载,则取代 (1-2) 得到调用,除了在删除不完整类型的对象、非类类型的数组和可平凡析构类类型的数组时,调用 (1-2) 还是 (5-6) 是未指定的。内存分配器可用给定的大小变得更高效。标准库实现等同于 (1-2) 。
7,8) 同 (5-6) ,除了若对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__
才被调用
9) 若对象的构造函数抛出异常,则为不抛出的单对象 new 表达式所调用。标准库实现表现同 (1)
10) 若任何对象的构造函数抛出异常,则(在执行数组中已成功构造的所有对象的析构函数后)为不抛出的数组 new[] 表达式所调用。标准库实现表现同 (2)
11,12) 同 (9,10) ,除了若对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__
才被调用
13) 若对象的构造函数抛出异常,则为标准单对象布置 new 表达式所调用。此函数的标准库实现不做任何事。
14) 若任何对象的构造函数抛出异常,则为布置 new 表达式的数组形式(在执行数组中已成功构造的所有对象的析构函数后)所调用。此函数的标准库实现不做任何事。
15) 若定义,则为拥有匹配签名的自定义单对象布置 new 表达式调用,若对象构造函数抛出异常。若定义类特定版本 (25) ,则类特定版本优先于 (9) 调用。若用户既不提供 (25) 又不提供 (15) ,则不调用解分配函数。
16) 若定义,则为拥有匹配签名的布置 new[] 表达式自定义数组形式(在执行数组中已成功构造的所有对象的析构函数后)调用,若任何对象的构造函数抛出异常。若定义类特定版本 (16) ,则类特定版本优先于 (10) 调用。若用户既不提供 (26) 又不提供 (16) ,则不调用解分配函数。
17) 若定义,则为通常单对象 delete 表达式调用,若在解分配一个 T 类型对象。
18) 若定义,则为通常数组 delete[] 表达式调用,若在解分配 T 类型对象的数组。
19,20) 若定义,则优先于 (17,18) 调用,若对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__
。
21) 若定义,且若未定义 (17) ,则为通常单对象 delete 表达式调用,若在解分配一个 T 类型对象。
22) 若定义,且若未定义 (18) ,则为通常数组 delete[] 表达式调用,若在解分配 T 类型对象的数组。
23,24) 若定义,且若未定义 (19,20) ,则优先于无对齐成员调用,若对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__
.
25) 若定义,则为拥有匹配签名的自定义单对象布置 new 表达式调用,若对象构造函数抛出异常。若不提供此函数,亦不提供匹配的 (15) ,则不调用解分配函数。
26) 若定义,则为拥有匹配签名的自定义布置 new[] 表达式的数组形式(在执行数组中已成功构造的所有对象的析构函数后)调用,若任何对象的构造函数抛出异常。若不提供此函数,亦不提供匹配的 (16) ,则不调用析构函数。
27-30) 若定义,则 delete 表达式在调用 operator delete
前不对 *p 执行析构函数。取而代之地,变为由此用户定义的 operator delete 负责直接调用析构函数,例如用 p->~T(); 。
通常(非布置)解分配函数的具对齐和不具对齐重载之间的重载决议准确细节见 delete 表达式。 | (C++17 起) |
所有情况下,若 ptr
是空指针,则标准库解分配函数不做任何事。若传递给标准库解分配函数的指针不是从对应的标准库分配函数取得者,则行为未定义。
在标准库解分配函数返回后,所有引用到被解分配存储的任何部分的指针都变为非法。
已因此方式变为非法的指针的任何使用,即使是复制指针值到另一指针变量,都是未定义行为。 | (C++14 前) |
通过已因此方式变为非法的指针解引用,以及将它传递给解分配函数(双重 delete )是未定义行为。任何其他使用是实现定义的。 | (C++14 起) |
参数
ptr | - | 指向要解分配的内存块的指针或空指针 |
sz | - | 传递给匹配的分配函数的 size |
place | - | 用作匹配的布置 new 中布置参数的指针 |
tag | - | 匹配不抛出 operator new 所用标签的重载消歧义标签 |
al | - | 被分配的对象或数组元素的对齐 |
args | - | 匹配布置分配函数的任意参数(可包含 std::size_t 与 std::align_val_t ) |
返回值
(无)
异常
15-30) 容许用户定义的解分配函数抛异常。 | (C++11 前) |
15-30) 所有解分配函数均为 | (C++11 起) |
全局替换
可替换解分配函数 (1-10) 在每个翻译单元隐式声明,即使不包含 <new>
头文件。这些函数是可替换的:定义于程序任何位置、任何头文件的用户提供的拥有相同签名的非成员函数,会为整个程序替换对应的隐式版本。其声明不必可见。
若程序中提供多于一个替换,或若替换声明有 inline
指定符,则行为未定义,若替换声明于异于全局命名空间的命名空间,或若它定义为在全局作用域的 static 非成员函数,则程序为病态。
nothrow 版本 (9,10) 的标准库实现直接调用抛出版本 (1,2) 。具大小解分配函数 (5-8) 的标准库实现直接调用对应的不具大小解分配函数 (1-4) 。不具大小的抛出数组形式 (2,4) 的标准库实现直接调用对应的单对象形式 (1,3) 。 故而,替换抛出的单对象解分配函数 (1,3) 足以处理所有解分配。 | (C++11 起) |