operator new, operator new[]

< cpp‎ | memory‎ | new
 
 
工具库
通用工具
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中弃用)
整数比较函数
(C++20)
swap 与类型运算
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
常用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++17)

初等字符串转换
(C++17)
(C++17)
 
动态内存管理
智能指针
(C++11)
(C++11)
(C++11)
(C++17 前)
(C++11)
分配器
内存资源
未初始化存储
未初始化内存算法
有制约的未初始化内存算法
垃圾收集支持
杂项
(C++20)
(C++11)
(C++11)
C 库
低层内存管理
 
 
定义于头文件 <new>
可替换分配函数

[[nodiscard]]

(C++20 起)
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 起)
可替换不抛出分配函数

noexcept

(C++11 起)

[[nodiscard]]

(C++20 起)
void* operator new  ( std::size_t count, const std::nothrow_t& tag);
(5)
void* operator new[]( std::size_t count, const std::nothrow_t& tag);
(6)
void* operator new  ( std::size_t count,
                      std::align_val_t al, const std::nothrow_t&);
(7) (C++17 起)
void* operator new[]( std::size_t count,
                      std::align_val_t al, const std::nothrow_t&);
(8) (C++17 起)
不分配布置分配函数

noexcept

(C++11 起)

[[nodiscard]]

(C++20 起)
void* operator new  ( std::size_t count, void* ptr );
(9)
void* operator new[]( std::size_t count, void* ptr );
(10)
用户定义布置分配函数
void* operator new  ( std::size_t count, user-defined-args... );
(11)
void* operator new[]( std::size_t count, user-defined-args... );
(12)
void* operator new  ( std::size_t count,
                      std::align_val_t al, user-defined-args... );
(13) (C++17 起)
void* operator new[]( std::size_t count,
                      std::align_val_t al, user-defined-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, user-defined-args... );
(19)
void* T::operator new[]( std::size_t count, user-defined-args... );
(20)
void* T::operator new  ( std::size_t count,
                         std::align_val_t al, user-defined-args... );
(21) (C++17 起)
void* T::operator new[]( std::size_t count,
                         std::align_val_t al, user-defined-args... );
(22) (C++17 起)

分配请求数量的字节。这些分配函数为 new 表达式所调用,以分配将被初始化的对象所在的内存。亦可用常规函数调用语法调用它们。

1) 为非数组 new 表达式所调用,以分配为单个对象请求的存储。标准库实现从自由存储分配 count 字节。失败情况下,标准库实现调用 std::get_new_handler 所返回的函数指针,并重复尝试分配,直到 new 处理函数不返回或成为空指针,在该时刻抛出 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) 在对象对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 时为不抛出非数组 new 表达式所调用。标准库实现调用版本 (3) 并在失败时不传播异常而抛出空指针。
8) 在数组元素的对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 时为 new[] 表达式的不抛出数组形式所调用。标准库实现调用版本 (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) 若定义,则为拥有匹配签名的自定义单对象布置 new 表达式所调用,若在分配 T 类型对象,且其对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 。若不提供此重载,但提供了无对齐成员形式 (19) ,则调用无对齐成员重载。
22) 若定义,则为拥有匹配签名的自定义数组形式布置 new 表达式所调用,若在分配 T 类型的对象数组,且其对齐要求超出 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 。若不提供此重载,但提供了无对齐成员形式 (20) ,则调用无对齐成员重载。

参数

count - 要分配的字节数
ptr - 指向要初始化的对象所在的内存区域的指针
tag - 用于选择不抛出重载的消歧义标签
al - 使用的对齐。若此非有效对齐值,则行为未定义

返回值

1-4) 非空指针,指向拥有至少 size 大小的适当对齐的内存
5-8) 非空指针,指向拥有至少 size 大小的适当对齐的内存,或在失败时为空指针
9-10) ptr
11-22) 若函数在分配失败时不返回则同 (1-4) ,否则同 (5-8)

异常

1-4) 在分配内存失败时抛出 std::bad_alloc 或任何从 std::bad_alloc 派生的异常 (C++11 起)
11-22) 若函数在分配失败时不返回则同 (1-4) ,否则同 (5-8)

全局替换

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

若程序中为任何可替换分配函数提供多于一个替换版本,或若替换定义带 inline 指定符,则行为未定义。若替换定义于异于全局命名空间的命名空间中,或它定义为在全局作用域的 static 函数,则程序为病式。

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

(C++11 起)
#include <cstdio>
#include <cstdlib>
// 函数最小集的替换:
void* operator new(std::size_t sz) {
    std::printf("global op new called, size = %zu\n",sz);
    void *ptr = std::malloc(sz);
    if (ptr)
        return ptr;
    else
        throw std::bad_alloc{};
}
void operator delete(void* ptr) noexcept
{
    std::puts("global op delete called");
    std::free(ptr);
}
int main() {
     int* p1 = new int;
     delete p1;
 
     int* p2 = new int[10]; // C++11 中保证调用替换者
     delete[] p2;
}

可能的输出:

global op new called, size = 4
global op delete called
global op new called, size = 40
global op delete called

operator newoperator 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*)

布置形式 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 起)
#include <iostream>
// 类特定分配函数
struct X {
    static void* operator new(std::size_t sz)
    {
        std::cout << "custom new for size " << sz << '\n';
        return ::operator new(sz);
    }
    static void* operator new[](std::size_t sz)
    {
        std::cout << "custom new for size " << sz << '\n';
        return ::operator new(sz);
    }
};
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 起)
#include <stdexcept>
#include <iostream>
struct X {
    X() { throw std::runtime_error(""); }
    // 自定义布置 new
    static void* operator new(std::size_t sz, bool b) {
        std::cout << "custom placement new called, b = " << b << '\n';
        return ::operator new(sz);
    }
    // 自定义布置 delete
    static void operator delete(void* ptr, bool b)
    {
        std::cout << "custom placement delete called, b = " << b << '\n';
        ::operator delete(ptr);
    }
};
int main() {
   try {
     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::malloc std::aligned_alloc (C++17 起) 的调用是未指定的。

示例

参阅

解分配函数
(函数)
获得当前的 new 处理函数
(函数)
注册一个 new 处理函数
(函数)
(C++17 中弃用)(C++20 中移除)
获得未初始化存储
(函数模板)
分配内存
(函数)
分配对齐的内存
(函数)

引用

  • C++11 standard (ISO/IEC 14882:2011):
  • 18.6 Dynamic memory management [support.dynamic]