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
|
尝试分配请求的字节数,且分配请求可能失败(即使请求的字节数为零)。这些分配函数由 new 表达式 调用,用于分配新对象初始化所需的内存。也可以通过常规函数调用语法来调用它们。
重载版本 ( 1-4 ) 会在每个翻译单元中隐式声明,即使未包含 <new> 头文件。
参见 new expression 了解选择重载的标准。
目录 |
参数
| count | - | 要分配的字节数 |
| ptr | - | 指向用于初始化对象的内存区域的指针 |
| tag | - | 用于选择非抛出重载的消歧义标签 |
| al | - | 使用的对齐方式,无效值将导致未定义行为 |
返回值
异常
全局替换
重载版本 ( 1-8 ) 是 可替换的 。默认版本的效果如下:
- 若尝试成功,则返回指向已分配存储空间的指针。
- 否则,若当前未安装 new-handler ,则抛出 std::bad_alloc 。
-
否则,调用当前安装的 new-handler。
- 若 new-handler 返回,则开始新一轮分配尝试。
- 否则,终止当前调用。
- 若调用正常返回,则返回该调用的结果。
- 否则返回空指针。
|
在 独立实现 中,默认版本的 ( 1-8 ) 是否满足上述要求的行为由实现定义。建议独立实现若其中任一默认版本满足托管实现的要求,则所有版本均应满足。 |
(since C++26) |
全局
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; }
可能的输出:
// 使用 GCC-5 在 C++17 模式下编译获得以下结果: 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 forms",版本
(
11-14
)
)的
operator new
和
operator new[]
重载可以在全局作用域中正常声明,并通过匹配的
placement 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 ) 是一个常规(非布局)释放函数。 |
(since C++14) |
类特定重载
单对象和数组分配函数均可定义为类的公共静态成员函数(版本 ( 15-18 ) )。若已定义,则 new 表达式将调用这些分配函数来为此类的单对象及数组分配内存,除非 new 表达式使用了跳过类作用域查找的 :: new 形式。对于这些函数,关键字 static 是可选的:无论是否使用该关键字,分配函数均为静态成员函数。
new 表达式首先在类作用域中查找适当的分配函数名称,然后在全局作用域中查找。请注意,根据 名称查找规则 ,在类作用域中声明的任何分配函数都会隐藏所有全局分配函数,对于试图分配该类对象的 new 表达式而言。
|
当分配对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象及对象数组时,会执行两次重载决议:首先针对对齐感知的函数签名,随后针对对齐无感知的函数签名。这意味着如果具有扩展对齐要求的类存在对齐无感知的类特定分配函数,被调用的将是该函数而非全局的对齐感知分配函数。这是有意为之的设计:类成员函数理应最了解如何处理该类的内存分配。 |
(since C++17) |
|
当分配对齐要求不超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象及对象数组时,会执行两次重载决议:首先针对不感知对齐的函数签名,随后针对感知对齐的函数签名。 |
(since C++20) |
#include <cstddef> #include <iostream> // 类特定的分配函数 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
具有额外用户定义参数("placement形式")的
operator new
和
operator new[]
重载,也可以定义为类的成员
(
19-22
)
。当具有匹配签名的placement
new
表达式寻找要调用的对应分配函数时,它会先在类作用域中查找,然后再检查全局作用域;如果提供了类特定的placement
new
,则会调用该函数。
|
当分配对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象及对象数组时,placement 形式的重载决议会像常规形式一样执行两次:首先针对对齐感知的函数签名,然后针对对齐无感知的函数签名。 |
(since C++17) |
|
当分配对齐要求不超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 的对象及对象数组时,placement 形式的重载决议会像常规形式一样执行两次:首先针对不感知对齐的函数签名,随后针对感知对齐的函数签名。 |
(since C++20) |
#include <cstddef> #include <iostream> #include <stdexcept> struct X { X() { throw std::runtime_error(""); } // 自定义 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); } // 自定义 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
,且必须包含两个或更多参数。换言之,只有 placement 形式的 operator new 才能作为模板函数。
注释
尽管不可替换的非分配布置 new ( 9,10 ) ,但如上所述可以在类作用域定义具有相同签名的函数。此外,允许存在形似布置 new 但以非void指针类型作为第二参数的全局重载,因此需要确保调用真正布置 new 的代码(例如 std::allocator::construct ),必须使用 :: new 并将指针强制转换为 void * 。
如果解分配函数的行为不满足默认约束,则行为是未定义的。
|
以下函数要求是线程安全的:
对这些函数分配或释放特定存储单元的调用以单一全序发生,且每个这样的释放调用 先于 该顺序中的下一个分配调用(如果存在)。 |
(since C++11) |
未指定库版本的
operator new
是否会调用
std::malloc
或
std::aligned_alloc
(C++17 起)
。
对于加载大型文件,通过操作系统特定函数进行文件映射,例如在POSIX系统上的
mmap
或在Windows系统上的
CreateFileMapping
(
A
/
W
)结合
MapViewOfFile
,相较于分配缓冲区进行文件读取更为可取。
| 功能测试 宏 | 值 | 标准 | 功能 |
|---|---|---|---|
__cpp_lib_freestanding_operator_new
|
202306L
|
(C++26) | 独立环境对可替换 operator new [1] 的支持 |
0
|
(C++26) | 无独立环境支持 | |
__cpp_lib_constexpr_new
|
202406L
|
(C++26) | constexpr placement new 与 new [ ] |
- ↑ 严格来说,当所有可替换全局分配函数的默认版本满足托管实现要求时,该宏展开为 202306L 。
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
| 缺陷报告 | 适用版本 | 发布时行为 | 正确行为 |
|---|---|---|---|
| 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 处理程序
(函数) |
|
注册 new 处理程序
(函数) |
|
|
(deprecated in C++17)
(removed in C++20)
|
获取未初始化存储空间
(函数模板) |
|
分配内存
(函数) |
|
|
(C++17)
|
分配对齐内存
(函数) |