new 式
動的記憶域期間を持つオブジェクト、つまり、生存期間が作成されたスコープに制限されないオブジェクトを、作成して初期化します。
目次 |
[編集] 構文
:: (オプション) new ( placement_params) (オプション) ( type ) initializer(オプション)
|
(1) | ||||||||
:: (オプション) new ( placement_params) (オプション) type initializer(オプション)
|
(2) | ||||||||
type
によって表される型のオブジェクトの作成を試みます。 型は配列型でも構いませんし、プレースホルダ型指定子を含んでも構いません (C++11以上)し、クラステンプレートの実引数推定によって推定される引数を持つクラステンプレートを含んでも構いません (C++17以上)。type
は括弧を含むことができません。new int(*[10])(); // エラー。 (new int) (*[10]) () としてパースされます。 new (int (*[10])()); // OK。 関数へのポインタ10個の配列。
さらに、括弧で囲っていない type は貪欲です。 宣言子の一部となり得るすべてのトークンを取り込みます。
new int + 1; // OK。 (new int) + 1、つまり new int によって返されたポインタをインクリメントします。 new int * 1; // エラー��� (new int*) (1) としてパースされます。
以下の場合、 initializer
はオプショナルではありません。
-
type
でプレースホルダ (auto
またはdecltype(auto)
(C++14以上)) が使用されている。 -
type
が境界の未知な配列である。
auto p = new auto('c'); // char 型の単一のオブジェクトを作成します。 p は char* です。 double* p = new double[]{1,2,3}; // double[3] 型の配列を作成します。
[編集] 説明
new
式は記憶域の確保を試み、その後、その確保した記憶域内に単一の無名オブジェクトまたはオブジェクトの無名配列のいずれかを構築および初期化します。 new 式は、その構築されたオブジェクトを指すポインタ、またはオブジェクトの配列が構築された場合はその配列の最初の要素を指すポインタの prvalue を返します。
type
が配列型の場合、最初以外のすべての次元は正の整数定数式 (C++14未満) std::size_t 型の変換された定数式 (C++14以上)でなければなりませんが、最初の次元は std::size_t に変換可能な任意の式を使用できます (括弧なしの構文 (2) のときのみ)。 これはサイズが実行時に定義される配列を直接作成する唯一の方法です。 このような配列はしばしば動的配列と呼ばれます。
int n = 42; double a[n][5]; // エラー。 auto p1 = new double[n][5]; // OK。 auto p2 = new double[5][n]; // エラー、最初の次元のみ可変にできます。 auto p3 = new (double[n][5]); // エラー、構文 (1) は動的配列には使用できません。
以下の場合、最初の次元を指定する式はエラーです。
- その式が非クラス型であり、 std::size_t への変換の前のその値が負である。
- その式がクラス型であり、ユーザ定義の変換の後かつ標準の変換の前のその値が負である。
- その式の値が処理系定義の何らかの制限より大きい。
- その値が波括弧で囲まれた初期化子で提供される配列の要素数 (文字列リテラルの場合は終端の
'\0'
を含みます) より小さい。
これらのいずれかの理由のために最初の次元の値がエラーである場合、
- std::size_t への変換後、最初の次元がコア定数式の場合、プログラムは ill-formed です (コンパイル時エラーが発行されます)。
|
(C++14以上) |
|
(C++11以上) |
最初の次元のサイズがゼロの場合、それは受理され、確保関数が呼ばれます。
ノート: 1次元の動的配列については std::vector が似た機能性を提供します。
[編集] 確保
new 式は適切な確保関数を呼ぶことによって記憶域を確保します。 type
が非配列型の場合、関数の名前は operator new
です。 type
が配列型の場合、関数の名前は operator new[]
です。
確保関数で説明されている通り、 C++ プログラムはこれらの関数の対するグローバルな、およびクラス固有の、置き換えを提供しても構いません。 new 式がオプショナルな :: 演算子で始まる場合 (::new T や ::new T[n] など)、クラス固有の置き換えは無視されます (関数はグローバルスコープで名前探索されます。 そうでなく、 T
がクラス型であれば、名前探索は T
のクラススコープで開始されます。
確保関数を呼ぶとき、 new 式は、 std::size_t 型の最初の引数として、要求されるバイト数を渡します。 これは非配列の T
については正確に sizeof(T) です。
配列の確保は未規定なオーバーヘッドを生じることがあります。 これはある new の呼び出しから次の new の呼び出しで変わる可能性もあります。 new 式によって返されるポインタは確保関数によって返されたポインタからそのオーバーヘッドの値だけオフセットされます。 多くの処理系は、デストラクタを正しい回数呼ぶために delete[] 式によって使用される、配列内のオブジェクト数を格納するために、配列のオーバーヘッドを使用します。 さらに、 new 式が char、 unsigned char または std::byte の配列を確保するために使用された場合は、要求された配列のサイズより大きくないすべての型のオブジェクトの、その確保された配列に後ほど配置される場合の、正しいアライメントを保証するために、必要であれば確保関数に追加のメモリを要求することがあります。
new 式は置き換え可能な確保関数を通して行う確保を省略したり合成したりすることが認められています。 省略のケースでは、記憶域は確保関数の呼び出しを行うことなくコンパイラによって提供されるかもしれません (未使用の new 式を最適化によって除去することも許されています)。 合成のケースでは、以下の条件がすべて真の場合に、 new 式 E1 によって行われる確保を別の new 式 E2 のための追加の記憶域も提供するよう拡張するかもしれません。 1) E1 によって確保されるオブジェクトの生存期間が E2 によって確保されるオブジェクトの生存期間を厳密に含む。
2) E1 と E2 が同じ置き換え可能なグローバルな確保関数を呼ぶ。
3) 確保関数が例外を投げた場合、 E1 と E2 の例外が最初にキャッチされるハンドラが同じである。
この最適化は new 式が使用されるときにのみ許されることに注意してください。 置き換え可能な確保関数を他の方法で呼んだときには許されません。 例えば delete[] new int[10]; は最適化できますが operator delete(operator new(10)); はできません。 |
(C++14以上) |
[編集] 配置 new
placement_params
が提供された場合、それらは追加の引数として確保関数に渡されます。 このような確保関数は「配置 new」と呼ばれます。 第2引数を変更せず返すだけの標準の確保関数 void* operator new(std::size_t, void*) に由来しています。 この関数は確保された記憶域内にオブジェクトを構築するために使用できます。
char* ptr = new char[sizeof(T)]; // メモリを確保します。 T* tptr = new(ptr) T; // 確保された記憶域にオブジェクトを構築 (「配置」) します。 tptr->~T(); // 破棄します。 delete[] ptr; // メモリを解放します。
ノート: この機能は Allocator クラスのメンバ関数によってカプセル化されています。
|
(C++17以上) |
new T; // operator new(sizeof(T)) を呼びます。 // (C++17) または operator new(sizeof(T), std::align_val_t(alignof(T)))) を呼びます。 new T[5]; // operator new[](sizeof(T)*5 + overhead) を呼びます。 // (C++17) または operator new(sizeof(T)*5+overhead, std::align_val_t(alignof(T)))) を呼びます。 new(2,f) T; // operator new(sizeof(T), 2, f) を呼びます。 // (C++17) または operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f) を呼びます。
確保関数がヌルポインタを返した場合 (例外を投げないオーバーロードが選択された場合 (new(std::nothrow) T; など) に起き得ます)、 new 式は直ちに戻り、オブジェクトの初期化や解放関数の呼び出しを試みません。 標準の配置確保関数がヌルポインタを返した場合 (引数としてヌルポインタを渡した場合に起き得ます)、動作は未定義です。 (C++17以上)
[編集] 構築
new 式によって作成されたオブジェクトは以下のルールに従って初期化されます。
- 非配列の
type
の場合、取得されたメモリ領域に単一のオブジェクトが構築されます。
|
(C++11以上) |
- type が配列型の場合、オブジェクトの配列が初期化されます。
|
(C++11以上) |
|
(C++20以上) |
初期化が (例えばコンストラクタから) 例外を投げることによって終了した場合、 new 式が何らかの記憶域を確保していたならば、適切な解放関数 (非配列の type
に対しては operator delete、配列の type
に対しては operator delete[]) を呼びます。 new 式が ::new 構文を使用していたならば解放関数はグローバルスコープから名前探索され、そうでなく T
がクラス型であれば T
のスコープから名前探索されます。 失敗した確保関数が通常のもの (配置形式でない) であったならば、解放関数の名前探索は delete 式で説明されているルールに従います。 配置 new の失敗の場合は、対応する解放関数の最初を除いたすべての引数が配置 new の引数と同一でなければなりません。 解放関数の呼び出しは、以前確保関数から取得した値が最初の引数として渡され、アライメントがオプショナルなアライメント引数として渡され (C++17以上)、 (もしあれば) placement_params
が追加の配置引数として渡されて行われます。 解放関数が見付からない場合、メモリは解放されません。
[編集] メモリリーク
new 式によって作成されたオブジェクト (動的記憶域期間を持つオブジェクト) は、その new 式によって返されたポインタが対応する delete 式で使用されるまで、残存します。 ポインタの元の値が失われた場合、そのオブジェクトは到達不能となり、解放することはできません。 すなわち、メモリリークが発生します。
これはポインタが代入されたり、
int* p = new int(7); // 値 7 の動的確保された int p = nullptr; // メモリリーク
ポインタがスコープから外れたり、
void f() { int* p = new int(7); } // メモリリーク
例外が投げられたりすると発生することがあります。
void f() { int* p = new int(7); g(); // 例外を投げる可能性がある delete p; // 例外がなかった場合は OK } // g() が投げた場合はメモリリーク
動的確保されたオブジェクトの管理を単純化するため、 new 式の結果はしばしばスマートポインタ (std::auto_ptr (C++17未満)std::unique_ptr または std::shared_ptr (C++11以上)) に格納されます。 スマートポインタは上に示したような状況でも delete 式が実行されることを保証します。
[編集] キーワード
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1992 | C++14 | new (std::nothrow) int[N] could throw bad_array_new_length | changed to return a null pointer |
P1009R2 | C++11 | the array bound cannot be deduced in a new expression | deduction permitted |