New (C++)

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

new的使用稱為「new運算符表達式」，其內部實現分為兩步：

- 調用相應的operator new函數，動態分配記憶體。如果operator new不能成功獲得記憶體，則調用new_handler函數。如果沒有設置new_handler函數或者new_handler未能分配足夠記憶體，則拋出`std::bad_alloc`異常。「new運算符表達式」所調用的operator new函數，按照C++的名字查找規則，首先做依賴於實參的名字查找（即ADL規則），在要申請記憶體的數據類型T的內部、數據類型T定義處的命名空間查找；如果沒有查找到，則直接調用全局的::operator new函數。
- 在分配到的動態記憶體塊上初始化相應類型的對象並返回其首地址。如果調用構造函數初始化對象時拋出異常，則自動調用operator delete函數釋放已經分配到的記憶體。

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

new運算符表達式是C++的一種語言結構，不可重載。但使用者可重載operator new函數。

## new運算符表達式語法

### 普通的new運算符表達式

new的語法是:

```cpp
p_var = new typename;
```

其中`p_var`是已經定義的類型為`typename`的指針變量。

通過`new`初始化對象，使用下述語法：

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

其中`initializer`是傳遞給構造函數的實參表或初值。

### 動態生成對象陣列的new運算符表達式

`new`也可創建一個對象陣列，稱之為「array forms new」：

```cpp
p_var = new type [size];
```

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

```cpp
int *p_int = new int[3] {1,2,3};
```

如此生成的對象陣列，在釋放時必須調用`delete [ ]`表達式。例如

```cpp
 delete [] p_int ;
```

### 帶位置的new運算符表達式

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

```cpp
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();`然後再自行釋放記憶體。

舉例：

```cpp
#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    char buf[100];
    int *p=new (buf) int(101);
    cout<<*(int*)buf<<endl;
    return 0;
}
```

### 保證不拋出異常的new運算符表達式

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

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

或

```cpp
new (nothrow) Type[size];
```

其中`nothrow`是`std::nothrow_t`的一個實例

### 自行定製參數的new運算符表達式

new運算符的參數可以是任意合法類型的列表。由C++的重載機制來決定調用那個operator new。

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

或

```cpp
new (Type_list) Type[size];
```

### 帶位置的delete運算符表達式

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

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

```cpp
    void
    destroy (T * p, A & arena)
    {
        p->~T() ;   // First invoke the destructor explicitly.
        arena.deallocate(p) ;  // Then call the deallocator function directly.
    }
```

如此使用：

```cpp
A arena ;
T * p = new (arena) T ;
/* ... */
destroy(p, arena) ;
```

第二種辦法是分兩步顯式調用析構函數與帶位置的operator delete函數：

```cpp
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函數獲取的記憶體。以避免記憶體洩露。

例如：

```cpp
#include <cstdlib>
#include <iostream>
char buf[100];
struct A {} ;
struct E {} ;

class T {
public:
    T() { throw E() ; }
    void * operator new(std::size_t,const A &){
        std::cout << "Placement new called for class T." << std::endl;
        return buf;
    }
    void operator delete(void*, const A &)
    {
        std::cout << "Placement delete called for class T." << std::endl;
    }
} ;

void * operator new ( std::size_t, const A & )
    {std::cout << "Placement new called." << std::endl; return buf;}
void operator delete ( void *, const A & )
    {std::cout << "Placement delete called." << std::endl;}

int main ()
{
    A a ;
    try {
        T * p = new (a) T ;
        /* do something */
    } catch (E exp) {std::cout << "Exception caught." << std::endl;}
    return 0 ;
}
```

## operator new()的函數重載

使用new動態生成一個對象，實際上是調用了new運算符表達式。該運算符首先調用了operator new函數動態分配記憶體，然後調用類型的構造函數初始化這塊記憶體。new運算符是不能被重載的，但是下述各種operator new函數既可以作為全局函數重載，也可以作為類成員函數或作用域內的函數重載，即由編程者指定如何獲取記憶體。

### 普通的operator new(size_t size)函數

new運算符調用operator new函數動態分配記憶體。首先查找類內是否有operator new函數可供使用（即依賴於實參的名字查找）。operator new函數的參數是一個size_t類型，指明了需要分配記憶體的規模。operator new函數可以被每個C++類作為成員函數重載。也可以作為全局函數重載：

```cpp
void * operator new (std::size_t) throw(std::bad_alloc);
void operator delete(void*) throw();
```

記憶體需要回收的話,調用對應的operator delete函數。

例如，在new運算符表達式的第二步，調用構造函數初始化記憶體時如果拋出異常，異常處理機制在棧展開（stack unwinding）時，要回收在new運算符表達式的第一步已經動態分配到的記憶體，這時就會自動調用對應operator delete函數。

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

new Type運算符（array forms new），用來動態創建一個對象陣列。這需要調用陣列基類型內部定義的void* operator new (size_t)函數來分配記憶體。如果陣列基類型沒有定義該函數，則調用全局的void* operator new (size_t)函數來分配記憶體。

在中聲明了void* operator new (size_t)全局函數：

```cpp
void * operator new[] (std::size_t) throw(std::bad_alloc);
void operator delete[](void*) throw();
```

### void* operator new(size_t,void*)

operator new(size_t,void*)函數用於帶位置的new運算符調用。C++標準庫已經提供了operator new(size_t,void*)函數的實現，包含頭文件即可。這個實現只是簡單的把參數的指定的地址返回，帶位置的new運算符就會在該地址上調用構造函數來初始化對象：

```cpp
// 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 <new>`

對應的placement delete函數，只應在placement new運算符表達式在第二步調用構造函數拋出異常時被異常處理機制的棧展開操作自動調用。

### 保證不拋出異常的operator new函數

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

```cpp
void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
void operator delete(void*, const std::nothrow_t&) throw();
void operator delete[](void*, const std::nothrow_t&) throw();
```

### 自行定製參數的operator new函數

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

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

例如：

```cpp
char data[1000][sizeof(int)];
inline void* operator new(size_t ,int n)
{
        return data[n];
}
void foo(){ 
    int *p=new(6) int(102); //把整型对象创建在data的第六个单元上
}
```

## 參考文獻

- IBM Documentation describing C++'s operator new
- Microsoft Visual Studio operator new documentation