低层内存管理
new 表达式是创建拥有动态存储期对象或对象数组的仅有方式,即它们拥有不受制于创建所它们在的作用域的生存期。 new 表达式通过调用分配函数获得存储。 delete 表达式销毁最终导出对象或通过 new 表达式创造的数组,然后调用解分配函数。默认分配函数和默认解分配函数,及与之关联的函数、类型及对象声明于头文件 <new>
。
分配函数
|---------------------------------------------------------------------------------------------|------|-----------|
| 定义于头文件 <new>
| | |
| 可替换分配函数 [[nodiscard]] (C++20 起) | | |
| void* operator new ( std::size_t count ); | (1) | |
| void* operator new[]( std::size_t count ); | (2) | |
| void* operator new ( std::size_t count, std::align_val_t al); | (3) | (C++17 起) |
| void* operator new[]( std::size_t count, std::align_val_t al); | (4) | (C++17 起) |
| | | |
| 可替换不抛出分配函数 [[nodiscard]] (C++20 起) | | |
| void* operator new ( std::size_t count, const std::nothrow_t& tag); | (5) | |
| void* operator new[]( std::size_t count, const std::nothrow_t& tag); | (6) | |
| void* operator new ( std::size_t count, std::align_val_t al, const std::nothrow_t&); | (7) | (C++17 起) |
| void* operator new[]( std::size_t count, std::align_val_t al, const std::nothrow_t&); | (8) | (C++17 起) |
| | | |
| 不分配布置分配函数 [[nodiscard]] (C++20 起) | | |
| void* operator new ( std::size_t count, void* ptr ); | (9) | |
| void* operator new[]( std::size_t count, void* ptr ); | (10) | |
| | | |
| 用户定义布置分配函数 | | |
| void* operator new ( std::size_t count, user-defined-args... ); | (11) | |
| void* operator new[]( std::size_t count, user-defined-args... ); | (12) | |
| void* operator new ( std::size_t count, std::align_val_t al, user-defined-args... ); | (13) | (C++17 起) |
| void* operator new[]( std::size_t count, std::align_val_t al, user-defined-args... ); | (14) | (C++17 起) |
| | | |
| 类特定分配函数 | | |
| void* T::operator new ( std::size_t count ); | (15) | |
| void* T::operator new[]( std::size_t count ); | (16) | |
| void* T::operator new ( std::size_t count, std::align_val_t al ); | (17) | (C++17 起) |
| void* T::operator new[]( std::size_t count, std::align_val_t al ); | (18) | (C++17 起) |
| | | |
| 类特定布置分配函数 | | |
| void* T::operator new ( std::size_t count, user-defined-args... ); | (19) | |
| void* T::operator new[]( std::size_t count, user-defined-args... ); | (20) | |
| void* T::operator new ( std::size_t count, std::align_val_t al, user-defined-args... ); | (21) | (C++17 起) |
| void* T::operator new[]( std::size_t count, std::align_val_t al, user-defined-args... ); | (22) | (C++17 起) |
分配请求数量的字节。这些分配函数为 new 表达式所调用,以分配将被初始化的对象所在的内存。亦可用常规函数调用语法调用它们。
-
为非数组 new 表达式所调用,以分配为单个对象请求的存储。标准库实现从自由存储分配
count
字节。失败情况下,标准库实现调用 std::get_new_handler 所返回的函数指针,并重复尝试分配,直到 new 处理函数不返回或成为空指针,在该时刻抛出 std::bad_alloc 。要求此函数返回指向拥有请求大小的对象的适当对齐的指针。 -
为 new[] 表达式的数组形式所调用,以分配为数组请求的所有存储(包含可能的 new 表达式开销)。标准库实现调用版本 (1)
-
为非数组 new 表达式调用,以分配对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
的单个对象所要求的存储 -
为 new[] 表达式的数组形式调用,以分配对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
的对象数组要求的所有存储 -
为不抛出非数组 new 表达式所调用。标准库实现调用版本 (1) 并在失败时不传播异常而抛出空指针。
-
为 new[] 表达式的不抛出数组形式所调用。标准库实现调用版本 (2) 并在失败时不传播异常而抛出空指针。
-
在对象对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
时为不抛出非数组 new 表达式所调用。标准库实现调用版本 (3) 并在失败时不传播异常而抛出空指针。 -
在数组元素的对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
时为 new[] 表达式的不抛出数组形式所调用。标准库实现调用版本 (4) 并在失败时不传播异常而抛出空指针。 -
为标准单对象布置 new 表达式所调用,标准库实现不进行任何动作并返回不修改的
ptr
。 -
为标准数组形式布置 new 表达式所调用。标准库实现不进行任何动作并返回不修改的
ptr
。 -
若定义,则为拥有匹配签名的自定义单对象布置 new 表达式所调用。若定义类特定版本 (19) ,则优先于 (11) 调用类特定版本。若用户既不提供 (11) 又不提供 (19) ,则布置 new 表达式为病式。
-
若定义,则为拥有匹配签名的自定义数组形式布置 new 表达式所调用。若定义类特定版本 (20) ,则优先于 (12) 调用类特定版本。若用户既不提供 (12) 又不提供 (20) ,则布置 new 表达式为病式。
-
若定义,则为拥有匹配签名的自定义单对象布置 new 表达式所调用,若对象的对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
。若定义类特定版本( (15) 或 (17) ),则调用之。若既不提供类特定版本又不提供具对齐布置形式(此形式),则替而查找无对齐布置形式 (11) 。 -
若定义,则为拥有匹配签名的自定义数组形式布置 new 表达式所调用,若元素的对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
。若定义类特定版本((16) 或 (18) ),则调用之。若既不提供类特定版本又不提供具对齐布置形式(此形式),则替而查找无对齐布置形式 (12) 。 -
若定义,则为通常的单对象 new 表达式所调用,若在分配 T 类型对象。
-
若定义,则为通常的数组 new[] 表达式所调用,若在分配 T 类型对象的数组。
-
若定义,则为通常的单对象 new 表达式所调用,若在分配 T 类型对象,且其对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
。若不提供此重载,但提供了无对齐成员形式 (15) ,则调用无对齐成员重载。 -
若定义,通常的数组 new[] 表达式所调用,若在分配 T 类型的对象数组,且其对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
。若不提供此重载,但提供了无对齐成员形式 (16) ,则调用无对齐成员重载。 -
若定义,则为拥有匹配签名的自定义单对象布置 new 表达式所调用,若在分配 T 类型对象。
-
若定义,则为拥有匹配签名的自定义数组形式布置 new 表达式所调用,若在分配 T 类型对象的数组。
-
若定义,则为拥有匹配签名的自定义单对象布置 new 表达式所调用,若在分配 T 类型对象,且其对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
。若不提供此重载,但提供了无对齐成员形式 (19) ,则调用无对齐成员重载。 -
若定义,则为拥有匹配签名的自定义数组形式布置 new 表达式所调用,若在分配 T 类型的对象数组,且其对齐要求超出
__STDCPP_DEFAULT_NEW_ALIGNMENT__
。若不提供此重载,但提供了无对齐成员形式 (20) ,则调用无对齐成员重载。
参数
|-------|---|-----------------------|
| count | - | 要分配的字节数 |
| ptr | - | 指向要初始化的对象所在的内存区域的指针 |
| tag | - | 用于选择不抛出重载的消歧义标签 |
| al | - | 使用的对齐。若此非有效对齐值,则行为未定义 |
返回值
1-4) 非空指针,指向拥有至少 size
大小的适当对齐的内存
5-8) 非空指针,指向拥有至少 size
大小的适当对齐的内存,或在失败时为空指针
9-10) ptr
11-22) 若函数在分配失败时不返回则同 (1-4) ,否则同 (5-8)
异常
1-4) 在分配内存失败时抛出 std::bad_alloc 或任何从 std::bad_alloc 派生的异常 (C++11 起)
5-10)
|-----------------------|-----------|
| (无) | (C++11 前) |
| noexcept 规定: noexcept | (C++11 起) |
11-22) 若函数在分配失败时不返回则同 (1-4) ,否则同 (5-8)
全局替换
版本 (1-4) 在每个翻译单元隐式声明,即使不包含 <new>
头文件。版本 (1-8) 是可替换的:定义于程序任何位置,在任何源文件的用户提供的拥有相同签名的非成员函数,会替换默认版本。其声明不必可见。
若程序中为任何可替换分配函数提供多于一个替换版本,或若替换定义带 inline
指定符,则行为未定义。若替换定义于异于全局命名空间的命名空间中,或它定义为在全局作用域的 static 函数,则程序为病式。
|-----------------------------------------------------------------------------------------------------------|-----------|
| nothrow 版本的标准库实现 (5-8) 直接调用对应的抛出版本 (1-4) 。抛出的数组版本的标准库实现 (2,4) 直接调用对应的单对象版本 (1,3) 。故而,替换抛出单对象分配函数足以处理所有分配。 | (C++11 起) |