名稱空間
變體
操作

operator new, operator new[]

來自 cppreference.com
< 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 中已移除)
獲取未初始化儲存
(函式模板) [編輯]
分配記憶體
(函式) [編輯]
分配對齊記憶體
(函式) [編輯]