new式
動的な記憶域期間を持つオブジェクト、すなわち、その生存期間が必ずしも作成されたスコープに限定されないオブジェクトを作成し、初期化します。
目次 |
[編集] 構文
::(optional) new (型 ) new-initializer (optional) |
(1) | ||||||||
::(optional) new 型 new-initializer (optional) |
(2) | ||||||||
::(optional) new (placement-args ) (型 ) new-initializer (optional) |
(3) | ||||||||
::(optional) new (placement-args ) 型 new-initializer (optional) |
(4) | ||||||||
[編集] 説明
| type | - | 対象の型名 |
| new-initializer | - | 丸括弧で囲まれた式リスト または 波括弧で囲まれた初期化子リスト(C++11以降) |
| placement-args | - | 追加の配置引数 |
new 式は、メモリを確保し、その後、確保されたメモリ内に単一の未命名オブジェクト、またはオブジェクトの配列を構築および初期化しようとします。new 式は、構築されたオブジェクトへの prvalue ポインタ、またはオブジェクトの配列が構築された場合は配列の最初の要素へのポインタを返します。
構文 (1) または (3) は、type に丸括弧が含まれる場合に必要です。
new int(*[10])(); // error: parsed as (new int) (*[10]) () new (int (*[10])()); // okay: allocates an array of 10 pointers to functions
さらに、type は貪欲に解析されます。宣言子の一部となりうるすべてのトークンが含まれます。
new int + 1; // okay: parsed as (new int) + 1, increments a pointer returned by new int new int * 1; // error: parsed as (new int*) (1)
new-initializer は、以下の場合を除き、省略可能ではありません。
- type が未定義境界の配列である場合、
| (C++11以降) | |
|
(C++17以降) |
double* p = new double[]{1, 2, 3}; // creates an array of type double[3] auto p = new auto('c'); // creates a single object of type char. p is a char* auto q = new std::integral auto(1); // OK: q is an int* auto q = new std::floating_point auto(true) // ERROR: type constraint not satisfied auto r = new std::pair(1, true); // OK: r is a std::pair<int, bool>* auto r = new std::vector; // ERROR: element type can't be deduced
[編集] 動的配列
type が配列型の場合、最初の次元以外のすべての次元は正の整数定数式(C++14まで)変換済み定数式(型は std::size_t)(C++14以降)として指定する必要があり、ただし(括弧で囲まれていない構文 (2) および (4) を使用する場合のみ)、最初の次元は整数型、列挙型、または整数型または列挙型への単一の非明示的変換関数を持つクラス型の式(C++14まで)std::size_tに変換可能な任意の式(C++14以降) にすることができます。これは、実行時にサイズが定義される配列を直接作成する唯一の方法であり、そのような配列はしばしば「動的配列」と呼ばれます。
int n = 42; double a[n][5]; // error auto p1 = new double[n][5]; // OK auto p2 = new double[5][n]; // error: only the first dimension may be non-constant auto p3 = new (double[n][5]); // error: syntax (1) cannot be used for dynamic arrays
|
最初の次元の値(必要に応じて整数型または列挙型に変換される)が負の場合、動作は未定義です。 |
(C++11まで) |
|
最初の次元を指定する式の値が無効な場合は、以下のとおりです。
いずれかの理由で最初の次元の値が無効な場合、
|
(C++11以降) |
最初の次元がゼロであることは許容され、メモリ確保関数が呼び出されます。
|
|
(C++11以降) |
[編集] メモリ確保
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 式によって返されるポインタは、メモリ確保関数によって返されるポインタからその値だけオフセットされます。多くの実装では、配列のオーバーヘッドを使用して配列内のオブジェクト数を格納しており、これは delete[] 式が適切な数のデストラクタを呼び出すために使用されます。さらに、new 式が char、unsigned char、または std::byte の配列を確保するために使用される場合(C++17以降)、配置されるオブジェクトの正しいアラインメントを保証するために、後で確保された配列に配置される場合、必要に応じてメモリ確保関数に追加のメモリを要求する場合があります。
|
1)
E1 によって確保されたオブジェクトの生存期間は、E2 によって確保されたオブジェクトの生存期間を厳密に包含する。2)
E1 および E2 は、同じ置換可能なグローバルメモリ確保関数を呼び出す。3) 例外を投げるメモリ確保関数の場合、
E1 および E2 での例外は、同じハンドラで最初にキャッチされる。この最適化は、 |
(C++14以降) |
|
定数式の評価中、メモリ確保関数の呼び出しは常に省略されます。定数式で評価できるのは、それ以外の場合は置換可能なグローバルメモリ確保関数を呼び出すことになる |
(C++20以降) |
[編集] 配置 new
placement-args が提供されている場合、それらは追加の引数としてメモリ確保関数に渡されます。そのようなメモリ確保関数は、「配置 new」として知られています。標準のメモリ確保関数 void* operator new(std::size_t, void*) の後にこれらが続きます。この関数は、2番目の引数を変更せずにそのまま返します。これは、確保されたメモリ内にオブジェクトを構築するために使用されます。
// within any block scope... { // Statically allocate the storage with automatic storage duration // which is large enough for any object of type “T”. alignas(T) unsigned char buf[sizeof(T)]; T* tptr = new(buf) T; // Construct a “T” object, placing it directly into your // pre-allocated storage at memory address “buf”. tptr->~T(); // You must **manually** call the object's destructor // if its side effects is depended by the program. } // Leaving this block scope automatically deallocates “buf”.
注意: この機能は、Allocator クラスのメンバ関数によってカプセル化されています。
|
アラインメント要件が __STDCPP_DEFAULT_NEW_ALIGNMENT__ を超えるオブジェクト、またはそのようなオブジェクトの配列を確保する場合、 |
(C++17以降) |
new T; // calls operator new(sizeof(T)) // (C++17) or operator new(sizeof(T), std::align_val_t(alignof(T)))) new T[5]; // calls operator new[](sizeof(T)*5 + overhead) // (C++17) or operator new(sizeof(T)*5+overhead, std::align_val_t(alignof(T)))) new(2,f) T; // calls operator new(sizeof(T), 2, f) // (C++17) or operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
非例外を投げるメモリ確保関数(例: new(std::nothrow) T で選択されるもの)がメモリ確保の失敗によりヌルポインタを返した場合、new 式は直ちに返り、オブジェクトの初期化や解放関数の呼び出しは行いません。ヌルポインタが非確保の配置 new 式の引数として渡され、選択された標準の非確保配置メモリ確保関数がヌルポインタを返す場合、動作は未定義です。
[編集] 初期化
new 式によって作成されたオブジェクトは、以下の規則に従って初期化されます。
type が配列型でない場合、単一のオブジェクトは取得されたメモリ領域に構築されます。
|
(C++11以降) |
type が配列型の場合、オブジェクトの配列が初期化されます。
- new-initializer が存在しない場合、各要素はデフォルト初期化されます。
- 最初の次元がゼロであっても、仮の要素のデフォルト初期化の意味論的制約を満たす必要があります。
- new-initializer が丸括弧のペアの場合、各要素は値初期化されます。
- 最初の次元がゼロであっても、仮の要素の値初期化の意味論的制約を満たす必要があります。
|
(C++11以降) |
|
(C++20以降) |
[編集] 初期化の失敗
初期化が例外をスローして終了した場合(例: コンストラクタから)、プログラムは一致する解放関数を検索し、次に
- 適切な解放関数が見つかった場合、解放関数が呼び出されてオブジェクトが構築されていたメモリが解放されます。その後、例外は
new式のコンテキストで伝播し続けます。 - 一意に一致する解放関数が見つからない場合、例外の伝播はオブジェクトのメモリを解放しません。これは、呼び出されたメモリ確保関数がメモリを確保しない場合にのみ適切であり、それ以外の場合はメモリリークにつながる可能性が高いです。
一致する解放関数の検索のスコープは、次のように決定されます。
new式が::で始まらず、確保された型がクラス型Tまたはクラス型Tの配列のいずれかである場合、Tのクラススコープで解放関数の名前が検索されます。- それ以外の場合、または
Tのクラススコープで見つからなかった場合、解放関数の名前はグローバルスコープで検索することによって検索されます。
非配置メモリ確保関数の場合、通常の解放関数検索を使用して一致する解放関数が見つかります(delete 式を参照)。
配置メモリ確保関数の場合、一致する解放関数は、パラメータの数が同じであり、最初のパラメータ以外の各パラメータの型が、メモリ確保関数の対応するパラメータの型と(パラメータ変換後)同一でなければなりません。
- 検索で単一の一致する解放関数が見つかった場合、その関数が呼び出されます。それ以外の場合は、解放関数は呼び出されません。
- 検索で非配置解放関数が見つかり、その関数が配置解放関数として考慮された場合にメモリ確保関数に一致するものとして選択された場合、プログラムはill-formed(不正な形式)です。
いずれの場合も、一致する解放関数(存在する場合)は非削除であり(C++11以降)、new 式が登場する場所からアクセス可能でなければなりません。
struct S { // Placement allocation function: static void* operator new(std::size_t, std::size_t); // Non-placement deallocation function: static void operator delete(void*, std::size_t); }; S* p = new (0) S; // error: non-placement deallocation function matches // placement allocation function
解放関数が new 式で呼び出された場合(初期化失敗のため)、その関数に渡される引数は次のように決定されます。
- 最初の引数は、メモリ確保関数呼び出しから返された値(型は
void*)です。 - その他の引数(配置解放関数のみ)は、配置メモリ確保関数に渡された
placement-argsです。
実装がメモリ確保関数への呼び出しの一部として一時オブジェクトを導入したり、引数のコピーを作成したりすることが許可されている場合、割り当て関数と解放関数の両方の呼び出しで同じオブジェクトが使用されるかどうかは未指定です。
[編集] メモリリーク
new 式によって作成されたオブジェクト(動的記憶域期間を持つオブジェクト)は、new 式によって返されたポインタが対応するdelete 式で使用されるまで存続します。ポインタの元の値が失われた場合、オブジェクトは到達不能になり、解放できなくなります。これにより、メモリリークが発生します。
これは、ポインタが次の場合に発生する可能性があります。
int* p = new int(7); // dynamically allocated int with value 7 p = nullptr; // memory leak
ポインタが代入された場合。
void f() { int* p = new int(7); } // memory leak
ポインタがスコープを外れた場合。
void f() { int* p = new int(7); g(); // may throw delete p; // okay if no exception } // memory leak if g() throws
例外による場合。
動的に確保されたオブジェクトの管理を容易にするために、new 式の結果はしばしばスマートポインタに格納されます。 std::auto_ptr(C++17まで)std::unique_ptr、または std::shared_ptr(C++11以降)。これらのポインタは、上記の場合にdelete式が確実に実行されることを保証します。
[編集] 注記
Itanium C++ ABI は、作成される配列の要素型がトリビアルに破棄可能である場合、配列のメモリ確保オーバーヘッドはゼロである必要があることを要求しています。MSVCも同様です。
一部の実装(例: VS 2019 v16.7 より前の MSVC)では、要素型がトリビアルに破棄可能でない非確保配置配列 new で、非ゼロの配列メモリ確保オーバーヘッドが必要でしたが、これは CWG issue 2382 以降、準拠しなくなっています。
unsigned char、または std::byte の配列を作成する非確保配置配列 new 式(C++17以降) は、指定されたメモリ領域にオブジェクトを暗黙的に作成するために使用できます。これは、配列と重複するオブジェクトの生存期間を終了させ、次に配列内に暗黙的生存期間を持つ型のオブジェクトを暗黙的に作成します。
std::vector は、一次元動的配列に対して同様の機能を提供します。
[編集] キーワード
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| [編集] 不具合報告 | C++98 | CWG 74 | 最初の次元の値は整数型でなければならない |
| 列挙型も許可される | C++98 | CWG 299 最初の次元の値は |
整数型または列挙型である必要がある 整数型または列挙型への単一の 変換関数を持つクラス型も許可される |
| CWG 624 | C++98 | 確保されるオブジェクトのサイズが 実装定義の制限を超える場合 動作が未指定であった |
この場合、ストレージは取得されず、 例外がスローされる |
| CWG 1748 | C++98 | 非確保配置 new は 引数が null かどうかをチェックする必要がある |
null 引数に対する未定義の動作 |
| CWG 1992 | C++11 | new(std::nothrow) int[N] std::bad_array_new_length をスローする可能性があった |
ヌルポインタを返すように変更された |
| CWG 2102 | C++98 | 空の配列の初期化時に デフォルト/値初期化が必要かどうか不明確だった |
必要 |
| CWG 2382 | C++98 | 非確保配置配列 new は メモリ確保オーバーヘッドを必要とする可能性があった |
そのようなメモリ確保オーバーヘッドは禁止された |
| CWG 2392 | C++11 | 最初の次元が潜在的に評価されない場合でも プログラムがill-formedになる可能性があった |
この場合、well-formed |
| P1009R2 | C++11 | 配列の境界を new 式で推論できなかった |
推論が許可されるようになった |