Namespaces
Variants

operator new , operator new[]

From cppreference.net
< cpp ‎ | memory ‎ | new
Utilities library
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until 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

尝试分配请求的字节数,且分配请求可能失败(即使请求的字节数为零)。这些分配函数由 new 表达式 调用,用于分配新对象初始化所需的内存。也可以通过常规函数调用语法来调用它们。

1-8) 可替换的 分配函数。标准库为这些函数提供了默认实现,关于默认实现的效果,请参见 下文
9,10) 由标准 布置 new 表达式 调用。不执行任何操作并返回未修改的 ptr
如果通过定位 new 调用此函数且 ptr 为空指针,则行为未定义。
11-22) new 表达式调用的用户自定义分配函数。

重载版本 ( 1-4 ) 会在每个翻译单元中隐式声明,即使未包含 <new> 头文件。

参见 new expression 了解选择重载的标准。

目录

参数

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 的处理器相匹配的异常类型。
11-22) 若函数在分配失败时不返回,则与 ( 1-4 ) 相同,否则与 ( 5-8 ) 相同。

全局替换

重载版本 ( 1-8 ) 可替换的 。默认版本的效果如下:

1) 尝试分配请求的存储空间。该尝试是否涉及调用 std::malloc std::aligned_alloc 未作规定。
  • 若尝试成功,则返回指向已分配存储空间的指针。
  • 否则,若当前未安装 new-handler ,则抛出 std::bad_alloc
  • 否则,调用当前安装的 new-handler。
    • 若 new-handler 返回,则开始新一轮分配尝试。
    • 否则,终止当前调用。
2) 返回 operator new ( count )
3) (1)
4) 返回 operator new ( count, al )
5-8) 使用相同参数(除 tag 外)分别调用 (1-4)
  • 若调用正常返回,则返回该调用的结果。
  • 否则返回空指针。

独立实现 中,默认版本的 ( 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 [ ]
  1. 严格来说,当所有可替换全局分配函数的默认版本满足托管实现要求时,该宏展开为 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 的公开静态成员函数)
释放函数
(函数)
获取当前 new 处理程序
(函数)
注册 new 处理程序
(函数)
(deprecated in C++17) (removed in C++20)
获取未初始化存储空间
(函数模板)
分配内存
(函数)
分配对齐内存
(函数)