operator new, operator new[]
| 在标头 <new> 定义
|
||
| 可替换的分配函数 |
||
| (1) | ||
| (2) | ||
| (3) | (C++17 起) | |
| (4) | (C++17 起) | |
| 可替换的不抛出分配函数 |
||
| (5) | (C++11 起为 noexcept) | |
| (6) | (C++11 起为 noexcept) | |
| (7) | (C++17 起) | |
| (8) | (C++17 起) | |
| 不分配布置分配函数 |
||
| (9) | (C++11 起为 noexcept) (C++26 起为 constexpr) |
|
| (10) | (C++11 起为 noexcept) (C++26 起为 constexpr) |
|
| 用户定义布置分配函数 |
||
| (11) | ||
| (12) | ||
| (13) | (C++17 起) | |
| (14) | (C++17 起) | |
| 类特定分配函数 |
||
| (15) | ||
| (16) | ||
| (17) | (C++17 起) | |
| (18) | (C++17 起) | |
| 类特定布置分配函数 |
||
| (19) | ||
| (20) | ||
| (21) | (C++17 起) | |
| (22) | (C++17 起) | |
尝试分配请求数量的字节,而且分配请求可能会失败(即使请求分配的字节数为零)。这些分配函数会被 new 表达式调用,以分配将被初始化的对象所在的内存。它们也可以通过常规函数调用语法调用。
new 调用此函数而 ptr 是空指针,那么行为未定义。new 表达式调用的用户定义分配函数。即使不包含标头 <new>,重载 (1-4) 也会在每个翻译单元隐式声明。
选择重载的标准见 new 表达式。
参数
| count | - | 要分配的字节数 |
| ptr | - | 指向要初始化的对象所在的内存区域的指针 |
| tag | - | 用于选择不抛出重载的消歧义标签 |
| al | - | 使用的对齐,对齐值无效时行为未定义 |
返回值
size 大小的适当对齐的内存的非空指针 p0,它与之前返回的任何值 p1 均不相同,除非 p1 在返回后已经传递给了可替换的解分配函数;分配失败时不返回(此时会抛出异常,见下文)ptr异常
全局替换
- 如果尝试成功,那么返回到分配的存储的指针。
- 否则,如果当前没有安装 new 处理函数,那么就会抛出 std::bad_alloc。
- 否则就会调用当前安装的 new 处理函数。
- 如果 new 处理函数返回,那么就会重新尝试。
- 否则就会退出当前调用。
operator new(count)。operator new(count, al)。tag 以外的相同实参分别调用 (1-4)。
- 如果调用正常返回,那么返回该调用的结果。
- 否则返回空指针。
|
在独立实现上,(1-8) 的默认版本的行为是否满足以上要求由实现定义。建议独立实现在其中有任何默认版本满足宿主实现的要求的情况下,其他所有版本都应同时满足。 |
(C++26 起) |
替换全局 operator new/delete:
#include <cstdio>
#include <cstdlib>
#include <new>
// 无 inline,由 [replacement.functions]/3 要求
void* operator new(std::size_t sz)
{
std::printf("1) new(size_t), size = %zu\n", sz);
if (sz == 0)
++sz; // 避免 std::malloc(0),它可能会在成功时返回 nullptr
if (void *ptr = std::malloc(sz))
return ptr;
throw std::bad_alloc{}; // 由 [new.delete.single]/3 要求
}
// 无 inline,由 [replacement.functions]/3 要求
void* operator new[](std::size_t sz)
{
std::printf("2) new[](size_t), size = %zu\n", sz);
if (sz == 0)
++sz; // 避免 std::malloc(0),它可能会在成功时返回 nullptr
if (void *ptr = std::malloc(sz))
return ptr;
throw std::bad_alloc{}; // 由 [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]; // 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)
operator new 和 operator new[] 带额外用户定义参数的重载(“布置形式”,版本 (11-14))可以照常在全局作用域声明,而且会被匹配的布置形式 new 表达式调用。
标准库的 operator new 的不分配布置形式 (9,10) 不能被替换,而且只能在布置 new 表达式不使用 ::new 语法时才能通过提供类特定的带匹配签名的布置 new (19,20)自定义:void* T::operator new(size_t, void*) 或 void* T::operator new[](size_t, void*)。
|
不能使用布置形式 |
(C++14 起) |
类特定重载
单对象和数组分配函数都可以定义为类的公开静态成员函数(版本 (15-18)。有定义时 new 表达式会调用这些函数,以分配此类单个对象或数组的内存,除非 new 表达式使用 ::new 形式,这样就会跳过类作用域查找。对这些函数不需要使用关键词 static:不管是否使用,分配函数都是静态成员函数。
new 表达式首先在类作用域中查找适当的函数名,然后在全局作用域查找。注意,与每个名称查找规则一样,就试图分配此类对象的 new 表达式而言,任何在类作用域声明的分配函数都会隐藏所有全局分配函数。
|
在分配对齐超出 |
(C++17 起) |
|
在分配对齐没有超出 |
(C++20 起) |
#include <cstddef>
#include <iostream>
// 类特定分配函数
struct X
{
static void* operator new(std::size_t count)
{
std::cout << "大小为 " << count << " 的自定义 new\n";
return ::operator new(count);
}
static void* operator new[](std::size_t count)
{
std::cout << "大小为 " << count << " 的自定义 new[]\n";
return ::operator new[](count);
}
};
int main()
{
X* p1 = new X;
delete p1;
X* p2 = new X[10];
delete[] p2;
}
可能的输出:
大小为 1 的自定义 new
大小为 10 的自定义 new[]
operator new 和 operator new[] 带额外用户定义形参(“布置形式”)的重载也可以定义为类成员 (19-22)。在拥有匹配签名的布置 new 表达式查找要调用的对应分配函数时,查找在检验全局作用域前,从类作用域开始,且在提供了类特定的布置 new 时会调用它。
|
在分配对齐超出 |
(C++17 起) |
|
在分配对齐没有超出 |
(C++20 起) |
#include <cstddef>
#include <iostream>
#include <stdexcept>
struct X
{
X() { throw std::runtime_error(""); }
// 自定义布置 new
static void* operator new(std::size_t count, bool b)
{
std::cout << "调用自定义的布置 new,b = " << b << '\n';
return ::operator new(count);
}
// 自定义布置 delete
static void operator delete(void* ptr, bool b)
{
std::cout << "调用自定义的布置 delete,b = " << b << '\n';
::operator delete(ptr);
}
};
int main()
{
try
{
[[maybe_unused]] X* p1 = new (true) X;
}
catch (const std::exception&)
{}
}
输出:
调用自定义的布置 new,b = 1
调用自定义的布置 delete,b = 1
如果类级别的 operator new 是模板函数,那么它必须拥有返回类型 void*,第一实参类型 std::size_t,且它必须拥有两个或更多形参。换言之,只有布置形式可以是模板。
注解
尽管不分配布置 new (9,10) 不能被替换,但在上面描述的类作用域也可以定义拥有相同签名的函数。另外,可以使用像是布置 new 但接受非 void 指针类型作为第二实参的全局重载,所以希望确保调用真正的布置 new 的代码(例如 std::allocator::construct)必须使用 ::new 并将指针转型到 void*。
如果解分配函数的行为无法满足默认约束,则其行为未定义。
|
要求下列函数是线程安全的:
对这些分配或解分配特定存储单元的函数调用以单独全序出现,而并且在此顺序中,每个解分配调用先发生于下个分配(如果存在)。 |
(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 的布置 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 Dynamic memory management [support.dynamic]
- C++20 标准(ISO/IEC 14882:2020):
- 17.6 Dynamic memory management [support.dynamic]
- C++17 标准(ISO/IEC 14882:2017):
- 21.6 Dynamic memory management [support.dynamic]
- C++14 标准(ISO/IEC 14882:2014):
- 18.6 Dynamic memory management [support.dynamic]
- C++11 标准(ISO/IEC 14882:2011):
- 18.6 Dynamic memory management [support.dynamic]
- C++03 标准(ISO/IEC 14882:2003):
- 18.4 Dynamic memory management [lib.support.dynamic]
- C++98 标准(ISO/IEC 14882:1998):
- 18.4 Dynamic memory management [lib.support.dynamic]
参阅
[静态] (C++23) |
用 Allocator 分配内存 ( std::generator<Ref,V,Allocator>::promise_type 的公开静态成员函数)
|
| 解分配函数 (函数) | |
(C++11) |
获得当前的 new 处理函数 (函数) |
| 注册一个 new 处理函数 (函数) | |
(C++17 弃用)(C++20 移除) |
获得未初始化存储 (函数模板) |
| 分配内存 (函数) | |
(C++17) |
分配对齐的内存 (函数) |