命名空间
变体
操作

operator new, operator new[]

来自 cppreference.com
< cpp‎ | memory‎ | new
 
 
实用程序库
语言支持
类型支持 (基本类型,RTTI)
库特性测试宏 (C++20)
动态内存管理
程序实用程序
协程支持 (C++20)
可变参数函数
调试支持
(C++26)
三路比较
(C++20)
(C++20)(C++20)(C++20)
(C++20)(C++20)(C++20)
通用实用程序
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中已弃用)
整数比较函数
(C++20)(C++20)(C++20)   
(C++20)
交换类型操作
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
通用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
基本字符串转换
(C++17)
(C++17)

 
动态内存管理
未初始化内存算法
受限未初始化内存算法
分配器
垃圾回收支持
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)



 
 
定义在头文件 <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 表达式 调用,以分配内存,然后在其中初始化新对象。也可以使用常规函数调用语法调用它们。

1)new 调用,以分配单个对象所需的存储空间。
标准库实现从空闲存储中分配 count 字节。
如果失败,标准库实现将调用由 std::get_new_handler 返回的函数指针,并重复分配尝试,直到新处理程序不返回或成为空指针,此时它会抛出 std::bad_alloc
此函数需要返回一个指针,该指针适当对齐以指向请求大小的对象。
2)new[] 调用,以分配数组所需的所有存储空间(包括可能的 new 表达式开销)。
标准库实现调用版本 (1)
3)new 调用,以分配单个对象的所需存储空间,其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__
4)new[] 调用,以分配对象的数组所需的所有存储空间,其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__
5) 由非抛出 new 调用。
标准库实现调用版本 (1),并在失败时返回空指针,而不是传播异常。
6) 由非抛出 new[] 调用。
标准库实现调用版本 (2),并在失败时返回空指针,而不是传播异常。
7) 由非抛出 new 调用,当对象的对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 时。
标准库实现调用版本 (3),并在失败时返回空指针,而不是传播异常。
8) 由非抛出 new[] 调用,当数组元素的对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 时。
标准库实现调用版本 (4),并在失败时返回空指针,而不是传播异常。
9) 由标准 放置 new 调用。
标准库实现不执行任何操作,并返回 ptr 不修改。
如果通过放置 new 调用此函数,并且 ptr 是空指针,则行为未定义。
10) 由标准放置 new[] 调用。
标准库实现不执行任何操作,并返回 ptr 不修改。
如果通过放置 new[] 调用此函数,并且 ptr 是空指针,则行为未定义。
11) 如果已定义,则由具有匹配签名的自定义放置 new 调用。
如果定义了类特定版本 (19),则优先调用它而不是 (11)
如果用户没有提供 (11)(19),则放置 new 形成不良。
12) 如果已定义,则由具有匹配签名的自定义放置 new[] 调用。
如果定义了类特定版本 (20),则优先调用它而不是 (12)
如果用户没有提供 (12)(20),则放置 new[] 形成不良。
13) 如果已定义,则由具有匹配签名的自定义放置 new 调用,如果对象的对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__
如果定义了类特定版本((15)(17)),则改为调用它。
如果没有提供类特定或全局对齐感知(此一个)放置形式,则改为查找对齐不感知的放置形式 (11)
14) 如果已定义,则由具有匹配签名的自定义放置 new[] 调用,如果元素的对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__
如果定义了类特定版本((16)(18)),则改为调用它。
如果没有提供类特定或全局对齐感知(此一个)放置形式,则改为查找对齐不感知的放置形式 (12)
15) 如果已定义,则由 new 调用,如果分配类型为 T 的对象。
16) 如果已定义,则由 new[] 调用,如果分配类型为 T 的对象的数组。
17) 如果已定义,则由 new 调用,如果分配类型为 T 的对象,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__
如果没有提供此重载,但提供了对齐不感知的成员形式 (15),则改为调用对齐不感知的成员重载。
18) 如果已定义,则由 new[] 调用,如果分配类型为 T 的对象的数组,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__
如果没有提供此重载,但提供了对齐不感知的成员形式 (16),则改为调用对齐不感知的成员重载。
19) 如果已定义,则由具有匹配签名的自定义放置 new 调用,如果分配类型为 T 的对象。
20) 如果已定义,则由具有匹配签名的自定义放置 new[] 调用,如果分配类型为 T 的对象的数组。
21) 如果定义了,当分配类型为 T 的对象时,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__,则由具有匹配签名的自定义放置 new 调用。
如果未提供此重载,但提供了与对齐无关的成员形式 (19),则改为调用与对齐无关的成员重载。
22) 如果定义了,当分配类型为 T 的对象的数组时,如果其对齐要求超过 __STDCPP_DEFAULT_NEW_ALIGNMENT__,则由具有匹配签名的自定义放置 new[] 调用。
如果未提供此重载,但提供了与对齐无关的成员形式 (20),则改为调用与对齐无关的成员重载。

内容

[编辑] 参数

count - 要分配的字节数
ptr - 指向要初始化对象的内存区域的指针
tag - 用于选择非抛出重载的区分标记
al - 要使用的对齐方式,无效值会导致未定义的行为

[编辑] 返回值

1-4) 如果分配成功,则返回一个非空指针 p0,它指向大小至少为 size 的适当对齐的内存,并且与之前返回的任何值 p1 不同,除非该值 p1 随后传递给可替换的 释放函数;如果分配失败,则不返回(抛出异常,见下文)。
5-8)(1-4) 相同,但如果分配失败则返回空指针。
9,10) ptr
11-22) 如果函数在分配失败时不返回,则与 (1-4) 相同,否则与 (5-8) 相同。

[编辑] 异常

1-4) 在分配内存失败时抛出类型为 std::bad_alloc 的异常,该异常将与类型为 std::bad_alloc 的处理程序匹配。
11-22) 如果函数在分配失败时不返回,则与 (1-4) 相同,否则与 (5-8) 相同。

[编辑] 全局替换

即使未包含 <new> 头文件,版本 (1-4) 也在每个翻译单元中隐式声明。版本 (1-8)可替换的:用户提供的具有相同签名的非成员函数,在程序中的任何位置、任何源文件中定义,都将替换默认版本。它的声明不需要可见。

如果程序中为任何可替换的分配函数提供了多个替换,或者如果用 inline 规范符 声明了替换,则程序格式不正确,不需要诊断。如果在全局命名空间以外的命名空间中定义了替换,或者如果将其定义为全局范围内的静态非成员函数,则程序格式不正确。

无抛出版本 (5-8) 的标准库实现直接调用相应的抛出版本 (1-4)。抛出数组版本 (2,4) 的标准库实现直接调用相应的单个对象版本 (1,3)。因此,替换抛出单个对象分配函数足以处理所有分配。

独立实现 中,默认版本 (1-8) 是否满足上面要求的行为是实现定义的。建议独立实现,如果这些默认版本中的任何一个满足托管实现的要求,则它们都应该满足。

(自 C++26 起)

全局 operators 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 newoperator 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 newoperator 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::mallocstd::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 放置 newnew[]
  1. 正式地,如果所有可替换的全局分配函数的默认版本都满足托管实现的要求,则此宏将扩展为 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 的公共静态成员函数) [编辑]
释放函数
(函数) [编辑]
获取当前 new 处理程序
(函数) [编辑]
注册新的处理程序
(函数) [编辑]
(C++17 中已弃用)(C++20 中已删除)
获取未初始化的存储
(函数模板) [编辑]
分配内存
(函数) [编辑]
分配对齐的内存
(函数) [编辑]