New (C++)

new 是 C++ 程式設計語言中的一種語言結構，用於動態分配記憶體、並用建構函式初始化分配的記憶體。

new 的使用稱為 “new 運算子表示式”，其內部實現分為兩步：

呼叫相應的 operator new 函式，動態分配記憶體。如果 operator new 不能成功獲得記憶體，則呼叫 newhandler 函式。如果沒有設定 newhandler 函式或者 newhandler 未能分配足夠記憶體，則丟擲 std::badalloc 異常。“new 運算子表示式” 所呼叫的 operator new 函式，按照 C++ 的規則，首先做依賴於實參的名字查詢（即 ADL 規則），在要申請記憶體的資料型別 T 的內部、資料型別 T 定義處的名稱空間查詢；如果沒有查詢到，則直接呼叫全域性的::operator new 函式。
在分配到的動態記憶體塊上初始化相應型別的物件並返回其首地址。如果呼叫建構函式初始化物件時丟擲異常，則自動呼叫 operator delete 函式釋放已經分配到的記憶體。

每個 new 獲取的物件，必須用 delete 析構並釋放記憶體，以免記憶體洩漏。

new 運算子表示式是 C++ 的一種語言結構，不可過載。但使用者可過載 operator new 函式。

new 運算子表示式語法

1.1 普通的 new 運算子表示式

new 的語法是:

p_var = new typename;

其中 p_var 是已經定義的型別為 typename 的指標變數。

透過 new 初始化物件，使用下述語法：

p_var = new type (initializer); // 或者如此初始化 new type {initializer};

其中 initializer 是傳遞給建構函式的實參表或初值。

1.2 動態生成物件陣列的 new 運算子表示式

new 也可建立一個物件陣列，稱之為 “array forms new”：

p_var = new type size;

C++98 標準規定，new 建立的物件陣列不能被顯式初始化， 陣列所有元素被預設初始化。如果陣列基型別沒有預設初始化，則編譯報錯。但 C++11 已經允許顯式初始化，例如：

int *p_int = new int3 {1,2,3};

如此生成的物件陣列，在釋放時必須呼叫 delete 表示式。例如 delete p_int ;

1.3 帶位置的 new 運算子表示式

帶位置的 new （placement new）的語法是：

new (expression-list) new-type-id (optional-initializer-expression-list);

其中，expression-list 將作為 operator new 函式的實參列表的結尾部分。這種形式的 new 運算子表示式首先呼叫 operator new (size_t,OtherTypeList) 函式來獲取記憶體；然後對該物件執行建構函式。這裡的 OtherTypeList 作為形參列表要和 new 表示式中第一個括號裡的實參列表 expression-list 的型別相容（即形參實參能夠匹配）。

帶位置的 new 運算子，語義上包括四種使用情形，其中前兩種已經在標準標頭檔案中實現了：

直接給出要構建的物件的記憶體位置；
不丟擲異常，如果記憶體分配失敗返回空指標；
定製的、帶其他引數的記憶體分配器；
用於除錯目的，在建構函式呼叫失敗時給出原始檔名與行號。

狹義上的帶位置的 new 是指第一種情形。使用這種 placement new，原因之一是使用者的程式不能在一塊記憶體上自行呼叫其建構函式（即使用者的程式不能顯式呼叫建構函式），必須由編譯系統生成的程式碼呼叫建構函式。原因之二是可能需要把物件放在特定硬體的記憶體地址上，或者放在多處理器核心的共享的記憶體地址上。

釋放這種物件時，不能呼叫 placement delete，應直接呼叫解構函式，如：pObj->~ClassType ; 然後再自行釋放記憶體。

舉例：

include

using namespace std;

int main (int argc, char* argv)

{

char buf100;

int *p=new (buf) int (101);

cout

1.4 保證不丟擲異常的 new 運算子表示式

在分配記憶體失敗時，new 運算子的標準行為是丟擲 std::bad_alloc 異常。也可以讓 new 運算子在分配記憶體失敗時不丟擲異常而是返回空指標。

new (nothrow) Type (optional-initializer-expression-list);

或

new (nothrow) Typesize;

其中 nothrow 是 std::nothrow_t 的一個例項

1.5 自行定製引數的 new 運算子表示式

new 運算子的引數可以是任意合法型別的列表。由 C++ 的過載機制來決定呼叫那個 operator new。

new (Type_list) Type (optional-initializer-expression-list);

或

new (Type_list) Typesize;

1.6 帶位置的 delete 運算子表示式

C++ 不能使用帶位置的 delete 運算子表示式直接析構一個物件但不釋放其記憶體。因此，對於用廣義的帶位置 new 表示式構建的物件，析構釋放時有兩種辦法：

第一種辦法是直接寫一個函式，完成析構物件、釋放記憶體的操作：

void

destroy (T * p, A & arena)

{p->~T ; // First invoke the destructor explicitly.

arena.deallocate (p) ; // Then call the deallocator function directly.

}

如此使用：

A arena ;

T * p = new (arena) T ;

/ ... /

destroy (p, arena) ;

第二種辦法是分兩步顯式呼叫解構函式與帶位置的 operator delete 函式：

A arena ;

T * p = new (arena) T ;

/ ... /

p->~T ; // First invoke the destructor explicitly.

operator delete (p, arena) ; // Then call the deallocator function indirectly via operator delete (void *, A &) .

帶位置的 operator delete 函式，可以被帶位置的 new 算符表示式自動呼叫。這是在物件的建構函式丟擲異常的時候，用來釋放掉帶位置的 operator new 函式獲取的記憶體。以避免記憶體洩露。

例如：

include

char buf100;

struct A ;

struct E ;

class T {

public:

T { throw E ; }

void * operator new (std::size_t,const A &){

std::cout

operator new 的函式過載

使用 new 動態生成一個物件，實際上是呼叫了 new 運算子表示式。該運算子首先呼叫了 operator new 函式動態分配記憶體，然後呼叫型別的建構函式初始化這塊記憶體。new 運算子是不能被過載的，但是下述各種 operator new 函式既可以作為全域性函式過載，也可以作為類成員函式或作用域內的函式過載，即由程式設計者指定如何獲取記憶體。

2.1 普通的 operator new (size_t size) 函式

new 運算子呼叫 operator new 函式動態分配記憶體。首先查詢類內是否有 operator new 函式可供使用（即依賴於實參的名字查詢）。operator new 函式的引數是一個 size_t 型別，指明瞭需要分配記憶體的規模。operator new 函式可以被每個 C++ 類作為成員函式過載。也可以作為全域性函式過載：

void * operator new (std::sizet) throw (std::badalloc);

void operator delete (void*) throw ;

記憶體需要回收的話，呼叫對應的 operator delete 函式。

例如，在 new 運算子表示式的第二步，呼叫建構函式初始化記憶體時如果丟擲異常，異常處理機制在棧展開（stack unwinding）時，要回收在 new 運算子表示式的第一步已經動態分配到的記憶體，這時就會自動呼叫對應 operator delete 函式。

2.2 陣列形式的 operator new (size_t size) 函式

new Type 運算子（array forms new），用來動態建立一個物件陣列。這需要呼叫陣列基型別內部定義的 void operator new (sizet) 函式來分配記憶體。如果陣列基型別沒有定義該函式，則呼叫全域性的 void operator new (sizet) 函式來分配記憶體。

在中聲明瞭 void* operator new (size_t) 全域性函式：

void * operator new (std::sizet) throw (std::badalloc);

void operator delete (void*) throw ;

2.3 void operator new (size_t,void)

operator new (sizet,void) 函式用於帶位置的 new 運算子呼叫。C++ 標準庫已經提供了 operator new (sizet,void) 函式的實現，包含標頭檔案即可。這個實現只是簡單的把引數的指定的地址返回，帶位置的 new 運算子就會在該地址上呼叫建構函式來初始化物件：

// Default placement versions of operator new.

inline void operator new (std::size_t, void p) throw { return p;}

inline void operator new (std::size_t, void p) throw { return p;}

// Default placement versions of operator delete.

inline void operator delete (void, void) throw

inline void operator delete (void, void) throw

禁止重定義這 4 個函式。因為都已經作為的內聯函數了。在使用時，實際上不需要 #include

對應的 placement delete 函式，只應在 placement new 運算子表示式在第二步呼叫建構函式丟擲異常時被異常處理機制的棧展開操作自動呼叫。

2.4 保證不丟擲異常的 operator new 函式

C++ 標準庫的中還提供了一個 nothrow 的實現，使用者可寫自己的函式替代：

void* operator new (std::sizet, const std::nothrowt&) throw ;

void* operator new (std::sizet, const std::nothrowt&) throw ;

void operator delete (void*, const std::nothrow_t&) throw ;

void operator delete (void*, const std::nothrow_t&) throw ;

2.5 自行定製引數的 operator new 函式

這種函式被自行定製引數的 new 算符呼叫。需要由使用者自行定義，以確定分配記憶體時的行為：

operator new (size_,Type1, Type2, ...);

例如：

char data1000sizeof (int);

inline void* operator new (size_t ,int n)

{return datan;}

void foo {int *p=new (6) int (102); // 把整型物件建立在 data 的第六個單元上

}