operator new, operator new[]
定义在头文件 <new> 中 |
||
可替换的分配函数 |
||
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 起) |
可替换的非抛出分配函数 |
||
void* operator new ( std::size_t count, const std::nothrow_t& tag ); |
(5) | (自 C++11 起为 noexcept) |
void* operator new[]( std::size_t count, const std::nothrow_t& tag ); |
(6) | (自 C++11 起为 noexcept) |
void* operator new ( std::size_t count, std::align_val_t al, const std::nothrow_t& tag ) noexcept; |
(7) | (自 C++17 起) |
void* operator new[]( std::size_t count, std::align_val_t al, const std::nothrow_t& tag ) noexcept; |
(8) | (自 C++17 起) |
非分配的放置分配函数 |
||
void* operator new ( std::size_t count, void* ptr ); |
(9) | (自 C++11 起为 noexcept) (自 C++26 起为 constexpr) |
void* operator new[]( std::size_t count, void* ptr ); |
(10) | (自 C++11 起为 noexcept) (自 C++26 起为 constexpr) |
用户定义的放置分配函数 |
||
void* operator new ( std::size_t count, /* args... */ ); |
(11) | |
void* operator new[]( std::size_t count, /* args... */ ); |
(12) | |
void* operator new ( std::size_t count, std::align_val_t al, /* args... */ ); |
(13) | (自 C++17 起) |
void* operator new[]( std::size_t count, std::align_val_t al, /* 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, /* args... */ ); |
(19) | |
void* T::operator new[]( std::size_t count, /* args... */ ); |
(20) | |
void* T::operator new ( std::size_t count, std::align_val_t al, /* args... */ ); |
(21) | (自 C++17 起) |
void* T::operator new[]( std::size_t count, std::align_val_t al, /* args... */ ); |
(22) | (自 C++17 起) |
尝试分配请求的字节数,分配请求可能会失败(即使请求的字节数为零)。这些分配函数由 new 表达式 调用,以分配内存,然后在其中初始化新对象。也可以使用常规函数调用语法调用它们。
T
的对象。T
的对象的数组。T
的对象,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__。T
的对象的数组,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__。T
的对象。T
的对象的数组。T
的对象时,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__,则由具有匹配签名的自定义放置 new 调用。T
的对象的数组时,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__,则由具有匹配签名的自定义放置 new[] 调用。内容 |
[编辑] 参数
count | - | 要分配的字节数 |
ptr | - | 指向要初始化对象的内存区域的指针 |
tag | - | 用于选择非抛出重载的区分标记 |
al | - | 要使用的对齐方式,无效值会导致未定义的行为 |
[编辑] 返回值
[编辑] 异常
[编辑] 全局替换
即使未包含 <new> 头文件,版本 (1-4) 也在每个翻译单元中隐式声明。版本 (1-8) 是可替换的:用户提供的具有相同签名的非成员函数,在程序中的任何位置、任何源文件中定义,都将替换默认版本。它的声明不需要可见。
如果程序中为任何可替换的分配函数提供了多个替换,或者如果用 inline 规范符 声明了替换,则程序格式不正确,不需要诊断。如果在全局命名空间以外的命名空间中定义了替换,或者如果将其定义为全局范围内的静态非成员函数,则程序格式不正确。
无抛出版本 (5-8) 的标准库实现直接调用相应的抛出版本 (1-4)。抛出数组版本 (2,4) 的标准库实现直接调用相应的单个对象版本 (1,3)。因此,替换抛出单个对象分配函数足以处理所有分配。
在 独立实现 中,默认版本 (1-8) 是否满足上面要求的行为是实现定义的。建议独立实现,如果这些默认版本中的任何一个满足托管实现的要求,则它们都应该满足。 |
(自 C++26 起) |
全局 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)
具有附加用户定义参数(“放置形式”,版本 (11-14))的 operator new
和 operator new[]
的重载可以像往常一样在全局范围内声明,并由 new 表达式的匹配 放置形式 调用。
标准库的非分配放置形式的 operator new
(9,10) 不可替换,并且只能在放置 new 表达式未使用 ::new 语法的情况下定制,方法是提供具有匹配签名的类特定放置 new (19,20):void* T::operator new(std::size_t, void*) 或 void* T::operator new[](std::size_t, void*).
放置形式 void* operator new(std::size_t, std::size_t) 不允许,因为释放函数的匹配签名,void operator delete(void*, std::size_t),是一个普通的(非放置)释放函数。 |
(自 C++14 起) |
[编辑] 类特定重载
单个对象和数组分配函数都可以定义为类的公共静态成员函数(版本 (15-18))。如果定义了,这些分配函数将由 new 表达式调用,以分配此类的单个对象和数组的内存,除非 new 表达式使用了 ::new 形式,该形式会绕过类范围查找。关键字 static 对这些函数是可选的:无论是否使用,分配函数都是一个静态成员函数。
new 表达式首先在类范围内查找合适的分配函数的名称,然后在全局范围内查找。注意,根据 名称查找规则,在类范围内声明的任何分配函数都会隐藏所有尝试分配此类对象的 new 表达式的全局分配函数。
当分配对齐方式超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象和对象数组时,将执行两次重载解析:首先,针对与对齐相关的函数签名,然后针对与对齐无关的函数签名。这意味着,如果具有扩展对齐方式的类具有与对齐无关的类特定分配函数,则将调用该函数,而不是全局与对齐相关的分配函数。这是故意的:类成员应该最了解如何处理该类。 |
(自 C++17 起) |
当分配对齐方式不超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象和对象数组时,将执行两次重载解析:首先,针对与对齐无关的函数签名,然后针对与对齐相关的函数签名。 |
(自 C++20 起) |
#include <cstddef> #include <iostream> // class-specific allocation functions struct X { static void* operator new(std::size_t count) { std::cout << "custom new for size " << count << '\n'; return ::operator new(count); } static void* operator new[](std::size_t count) { std::cout << "custom new[] for size " << count << '\n'; return ::operator new[](count); } }; int main() { X* p1 = new X; delete p1; X* p2 = new X[10]; delete[] p2; }
可能的输出
custom new for size 1 custom new[] for size 10
具有附加用户定义参数(“放置形式”)的 operator new
和 operator new[]
的重载也可以定义为类成员 (19-22))。当具有匹配签名的放置 new 表达式查找要调用的相应分配函数时,它将从类范围开始,然后检查全局范围,如果提供了类特定放置 new,则调用它。
当分配对齐方式超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象和对象数组时,放置形式的重载解析将像普通形式一样执行两次:首先,针对与对齐相关的函数签名,然后针对与对齐无关的函数签名。 |
(自 C++17 起) |
当分配对齐方式不超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象和对象数组时,放置形式的重载解析将像普通形式一样执行两次:首先,针对与对齐无关的函数签名,然后针对与对齐相关的函数签名。 |
(自 C++20 起) |
#include <cstddef> #include <iostream> #include <stdexcept> struct X { X() { throw std::runtime_error(""); } // custom placement new static void* operator new(std::size_t count, bool b) { std::cout << "custom placement new called, b = " << b << '\n'; return ::operator new(count); } // 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&) {} }
输出
custom placement new called, b = 1 custom placement delete called, b = 1
如果类级别的 operator new
是一个模板函数,它必须具有 void* 的返回类型,第一个参数为 std::size_t,并且它必须有两个或更多个参数。换句话说,只有放置形式可以是模板。
[编辑] 注意
尽管不能替换非分配放置 new (9,10),但如上所述,可以在类作用域内定义具有相同签名的函数。此外,允许类似于放置 new 但将非空指针类型作为第二个参数的全局重载,因此想要确保调用真正的放置 new(例如 std::allocator::construct)的代码必须使用 ::new 并将指针强制转换为 void*。
如果释放函数的行为不满足默认约束,则行为未定义。
以下函数要求线程安全
分配或释放特定存储单元的这些函数调用按单一总顺序发生,并且每个这样的释放调用在该顺序中的下一个分配(如果有)之前发生。 |
(自 C++11 起) |
库版本的 operator new
是否调用 std::malloc 或 std::aligned_alloc(自 C++17 起) 是未指定的。
对于加载大型文件,建议使用特定于操作系统的函数(例如 POSIX 上的 mmap
或 Windows 上的 CreateFileMapping
(A
/W
) 与 MapViewOfFile
)进行文件映射,而不是分配用于文件读取的缓冲区。
功能测试 宏 | 值 | Std | 功能 |
---|---|---|---|
__cpp_lib_freestanding_operator_new |
202306L | (C++26) | 可替换的 operator new 的独立支持[1] |
0 | (C++26) | 无独立支持 | |
__cpp_lib_constexpr_new |
202406L | (C++26) | constexpr 放置 new 和 new[] |
- ↑ 正式地,如果所有可替换的全局分配函数的默认版本都满足托管实现的要求,则此宏将扩展为 202306L。
[编辑] 缺陷报告
以下行为更改的缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确的行为 |
---|---|---|---|
CWG 521 | C++98 | 可以抛出从 std::bad_alloc 派生的任何类, 即使 std::bad_alloc 基类是模棱两可的或不可访问的 |
抛出的异常应匹配 类型为 std::bad_alloc 的处理程序 |
LWG 9 | C++98 | 分配零的多个调用 字节可能产生相同的指针 |
仅在所有此类以前 产生的指针已 传递给释放函数 |
LWG 206 | C++98 | 替换可替换分配函数 不会影响相应 可替换的非抛出分配函数的默认行为 |
默认行为 相应地更改 |
LWG 404 | C++98 | 可替换分配函数的替换 函数可以声明为 inline |
禁止,不需要诊断 |
[编辑] 参考资料
- C++23 标准 (ISO/IEC 14882:2024)
- 17.7 动态内存管理 [support.dynamic]
- C++20 标准 (ISO/IEC 14882:2020)
- 17.6 动态内存管理 [support.dynamic]
- C++17 标准 (ISO/IEC 14882:2017)
- 21.6 动态内存管理 [support.dynamic]
- C++14 标准 (ISO/IEC 14882:2014)
- 18.6 动态内存管理 [support.dynamic]
- C++11 标准 (ISO/IEC 14882:2011)
- 18.6 动态内存管理 [support.dynamic]
- C++03 标准 (ISO/IEC 14882:2003)
- 18.4 动态内存管理 [lib.support.dynamic]
- C++98 标准 (ISO/IEC 14882:1998)
- 18.4 动态内存管理 [lib.support.dynamic]
[编辑] 另请参见
[static] (C++23) |
使用 Allocator 分配内存( std::generator<Ref,V,Allocator>::promise_type 的公共静态成员函数) |
释放函数 (函数) | |
(C++11) |
获取当前 new 处理程序 (函数) |
注册新的处理程序 (函数) | |
(C++17 中已弃用)(C++20 中已删除) |
获取未初始化的存储 (函数模板) |
分配内存 (函数) | |
(C++17) |
分配对齐的内存 (函数) |