命名空间
变体
操作

operator new, operator new[]

来自 cppreference.cn
< cpp‎ | 内存‎ | new
 
 
 
内存管理库
(仅作说明*)
未初始化内存算法
(C++17)
(C++17)
(C++17)
受约束的未初始化
内存算法
C 库

分配器
内存资源
垃圾回收支持
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
未初始化存储
(直到 C++20*)
(直到 C++20*)
显式生命周期管理
 
 
定义于头文件 <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 起无异常抛出)
void* operator new[]( std::size_t count, const std::nothrow_t& tag );
(6) (C++11 起无异常抛出)
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 起无异常抛出)
(C++26 起为 constexpr)
void* operator new[]( std::size_t count, void* ptr );
(10) (C++11 起无异常抛出)
(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-8) 可替换的分配函数。标准库为这些函数提供了默认实现,有关默认实现的效果,请参见下文
9,10) 由标准就地 new 表达式 调用。不执行任何操作并返回未修改的 ptr
如果通过就地 new 调用此函数,并且 ptr 是空指针,则行为未定义。
11-22)new 表达式调用的用户定义分配函数。

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

有关选择重载的标准,请参见 new 表达式

目录

[编辑] 参数

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::mallocstd::aligned_alloc 是未指定的。
  • 如果尝试成功,则返回指向已分配存储的指针。
  • 否则,如果当前未安装新处理程序,则抛出 std::bad_alloc
  • 否则,调用当前安装的新处理程序。
    • 如果新处理程序返回,则开始另一次分配尝试。
    • 否则,退出当前调用。
2) 返回 operator new(count)
3)(1) 相同。
4) 返回 operator new(count, al)
5-8) 分别使用相同的参数调用 (1-4),除了 tag
  • 如果调用正常返回,则返回该调用的结果。
  • 否则,返回空指针。

独立实现上,(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)

带有额外用户定义参数的 operator newoperator new[] 重载(“就地形式”,版本 (11-14))可以像往常一样在全局作用域声明,并由匹配的 new 表达式的就地形式调用。

标准库的非分配就地形式的 operator new (9,10) 不能被替换,并且只能通过提供具有匹配签名的类特有就地 new (19,20) 来定制,如果就地 new 表达式没有使用 ::new 语法: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 但将非 void 指针类型作为第二个参数,因此希望确保调用真正的就地 new 的代码(例如 std::allocator::construct)必须使用 ::new 并将指针强制转换为 void*

如果解除分配函数的行为不满足默认约束,则行为未定义。

以下函数必须是线程安全的

这些函数分配或解除分配特定存储单元的调用以单一总顺序发生,并且每次此类解除分配调用都先于此顺序中的下一次分配(如果有)。

(C++11 起)

未指定库版本的 operator new 是否调用 std::mallocstd::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 就地 newnew[]
  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 处理函数
(函数) [编辑]
(在 C++17 中已弃用)(在 C++20 中已移除)
获取未初始化存储
(函数模板) [编辑]
分配内存
(函数) [编辑]
分配对齐内存
(函数) [编辑]