new
expression
動的 storage duration を持つオブジェクト、すなわちその寿命が作成されたスコープによって必ずしも制限されないオブジェクトを作成し初期化します。
目次 |
構文
::
(オプション)
new
(
type
)
new-initializer
(オプション)
|
(1) | ||||||||
::
(オプション)
new
type
new-initializer
(オプション)
|
(2) | ||||||||
::
(オプション)
new
(
placement-args
)
(
type
)
new-initializer
(オプション)
|
(3) | ||||||||
::
(オプション)
new
(
placement-args
)
type
new-initializer
(オプション)
|
(4) | ||||||||
説明
| type | - | 対象の型ID |
| new-initializer | - | 括弧で囲まれた式リスト または 波括弧で囲まれた初期化子リスト (C++11以降) |
| placement-args | - | 追加の配置引数 |
new
式は、ストレージの割り当てを試み、その後、割り当てられたストレージ内で単一の無名オブジェクト、または無名オブジェクトの配列を構築および初期化しようと試みます。
new
式は、構築されたオブジェクトへのprvalueポインタ、またはオブジェクトの配列が構築された場合は配列の先頭要素へのポインタを返します。
構文 (1) または (3) は、 type に括弧が含まれる場合に必要です:
new int(*[10])(); // エラー: (new int) (*[10]) () として解析される new (int (*[10])()); // 正常: 10個の関数ポインタの配列を確保する
さらに、 type は貪欲に解析されます:宣言子の一部となり得るすべてのトークンが含まれるように解釈されます:
new int + 1; // 正常: (new int) + 1 として解析され、new int で返されたポインタをインクリメント new int * 1; // エラー: (new int*) (1) として解析される
new-initializer は以下の場合に省略できません
- type は 境界未知の配列 です,
| (C++11以降) | |
|
(C++17以降) |
double* p = new double[]{1, 2, 3}; // double[3]型の配列を作成 auto p = new auto('c'); // char型の単一オブジェクトを作成。pはchar* auto q = new std::integral auto(1); // OK: qはint* auto q = new std::floating_point auto(true) // エラー: 型制約が満たされない auto r = new std::pair(1, true); // OK: rはstd::pair<int, bool>* auto r = new std::vector; // エラー: 要素型を推論できない
動的配列
type が配列型の場合、最初の次元以外のすべての次元は正の 整数定数式 (C++14まで) 変換済み定数式 (型 std::size_t へ) (C++14以降) で指定されなければならない。ただし(括弧なし構文 (2) および (4) を使用する場合のみ)、最初の次元は 整数型、列挙型、または整数型/列挙型への単一の非明示的変換関数を持つクラス型の式 (C++14まで) std::size_t へ変換可能な任意の式 (C++14以降) であってもよい。これは実行時にサイズが定義される配列を直接作成する唯一の方法であり、このような配列はしばしば 動的配列 と呼ばれる:
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)は動的配列に使用できません
|
最初の次元の値(必要に応じて整数型または列挙型に変換された)が負の場合、動作は未定義です。 |
(C++11以前) |
|
以下の場合、最初の次元を指定する式の値は無効です:
最初の次元の値がこれらの理由のいずれかで無効な場合:
|
(C++11以降) |
最初の次元がゼロであることは許容され、アロケーション関数が呼び出されます。
|
new-initializer が波括弧で囲まれた初期化子リストであり、かつ最初の次元が 潜在的に評価される 式であり、 コア定数式 ではない場合、空の初期化子リストからの コピー初期化 による仮想的な配列要素のセマンティック制約がチェックされる。 |
(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以降) の配列を割り当てるために使用される場合、後で割り当てられた配列に配置される、要求された配列サイズを超えないすべての型のオブジェクトの正しいアラインメントを保証するために必要であれば、割り当て関数から追加のメモリを要求する可能性があります。
|
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 式のみが定数式中で評価可能です。 |
(C++20以降) |
Placement new
placement-args が提供される場合、それらは追加の引数としてアロケート関数に渡されます。このようなアロケート関数は、標準的なアロケート関数 new にちなんで「プレースメント new 」として知られています。この標準アロケート関数は void * operator new ( std:: size_t , void * ) であり、単に第二引数を変更せずに返します。これは、割り当てられたストレージ内でオブジェクトを構築するために使用されます:
// 任意のブロックスコープ内で... { // 自動ストレージ期間を持つ静的ストレージを割り当てる // これは型「T」の任意のオブジェクトに十分な大きさを持つ alignas(T) unsigned char buf[sizeof(T)]; T* tptr = new(buf) T; // 「T」オブジェクトを構築し、事前に割り当てられた // ストレージのメモリアドレス「buf」に直接配置する tptr->~T(); // プログラムがその副作用に依存する場合は、 // オブジェクトのデストラクタを**手動で**呼び出す必要がある } // このブロックスコープを抜けると「buf」は自動的に解放される
注記: この機能は Allocator クラスのメンバ関数によってカプセル化されています。
|
__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超えるアライメント要件を持つオブジェクト、またはそのようなオブジェクトの配列を割り当てる場合、 new 式はアライメント要件( std::align_val_t でラップされたもの)を割り当て関数の第2引数として渡します(配置形式の場合、 placement-arg はアライメントの後に第3、第4引数などとして現れます)。オーバーロード解決が失敗した場合(クラス固有の割り当て関数が異なるシグネチャで定義されている場合に発生します。グローバル関数を隠すため)、引数リストからアライメントを除いて2回目のオーバーロード解決が試みられます。これにより、アライメントを考慮しないクラス固有の割り当て関数が、グローバルなアライメント対応割り当て関数よりも優先されるようになります。 |
(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 式は直ちに戻り、オブジェクトの初期化やデアロケート関数の呼び出しを試みません。ヌルポインタが非アロケーティング配置 new 式の引数として渡され、選択された標準の非アロケーティング配置アロケート関数がヌルポインタを返す場合、動作は未定義です。
初期化
new 式によって作成されるオブジェクトは、以下の規則に従って初期化されます。
type が配列型でない場合、単一のオブジェクトが取得したメモリ領域に構築されます:
|
(C++11以降) |
type が配列型の場合、オブジェクトの配列が初期化されます:
- new-initializer が存在しない場合、各要素は デフォルト初期化 されます。
-
- 最初の次元がゼロであっても、仮想的な要素をデフォルト初期化する際の意味論的制約は依然として満たされる必要があります。
- new-initializer が括弧のペアである場合、各要素は value-initialized されます。
-
- 最初の次元がゼロであっても、仮想的な要素を値初期化する際の意味論的な制約は依然として満たされる必要があります。
|
(C++11以降) |
|
(C++20以降) |
初期化失敗
初期化が例外を投げて終了した場合(例えばコンストラクタからの場合)、プログラムは対応する解放関数を検索し、次に:
- 適切な解放関数が見つかった場合、解放関数が呼び出され、オブジェクトが構築されていたメモリが解放されます。その後、例外は new 式のコンテキストで伝播を継続します。
- 明確に一致する解放関数が見つからない場合、例外の伝播によってオブジェクトのメモリが解放されることはありません。これは呼び出されたアロケーション関数がメモリを割り当てない場合にのみ適切であり、それ以外の場合はメモリリークを引き起こす可能性が高いです。
対応する解放関数の lookup のスコープは以下のように決定されます:
-
new
式が
::で始まらず、かつ割り当てられる型がクラス型Tまたはクラス型Tの配列である場合、Tのクラススコープで解放関数の名前が検索されます。 -
そうでない場合、または
Tのクラススコープで何も見つからなかった場合、解放関数の名前は グローバルスコープ で検索されます。
非配置配置関数の場合、通常の解放関数のルックアップが使用され、対応する解放関数が見つけられます( delete-expression を参照)。
配置用配置関数に対して、対応する解放関数は同じ数のパラメータを持たなければならず、最初のパラメータを除く各パラメータの型は、配置関数の対応するパラメータ型と同一でなければなりません( パラメータ変換 の後)。
- 検索が単一の一致する解放関数を見つけた場合、その関数が呼び出されます。それ以外の場合、解放関数は呼び出されません。
- 検索が非配置解放関数を見つけ、その関数が配置解放関数として考慮された場合にアロケーション関数のマッチとして選択されていたならば、プログラムは不適格です。
いずれの場合でも、対応する解放関数(もしあれば)は 削除されておらず、 (C++11以降) new 式が現れる地点からアクセス可能でなければなりません。
struct S { // 配置 new 割り当て関数: static void* operator new(std::size_t, std::size_t); // 非配置 delete 解放関数: static void operator delete(void*, std::size_t); }; S* p = new (0) S; // エラー: 非配置解放関数が // 配置割り当て関数と一致する
デアロケーション関数が new 式内で(初期化の失敗により)呼び出される場合、その関数に渡される引数は以下のように決定されます:
- 第1引数は、割り当て関数呼び出しから返された値(型 void * )です。
- その他の引数(配置解放関数のみ)は、配置割り当て関数に渡された placement-args です。
実装が一時オブジェクトを導入するか、または割り当て関数の呼び出しの一部として任意の引数のコピーを作成することが許可されている場合、割り当て関数と呼び出し解除関数の両方の呼び出しで同じオブジェクトが使用されるかどうかは未規定です。
メモリリーク
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; // 例外が発生しなければ問題なし } // g()がスローした場合、メモリリークが発生
動的に確保されたオブジェクトの管理を簡素化するため、 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 以降、規格に適合しなくなりました。
メモリを確保しない配置配列 new 式は、 unsigned char または std::byte (C++17以降) の配列を作成し、指定されたストレージ領域で オブジェクトを暗黙的に作成 するために使用できます:これは配列と重複するオブジェクトの寿命を終了させ、その後、暗黙的寿命型のオブジェクトを配列内に暗黙的に作成します。
std::vector は一次元動的配列に対して同様の機能を提供します。
キーワード
不具合報告
以下の動作変更欠陥報告書は、以前に公開されたC++標準に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 74 | C++98 | 最初の次元の値は整数型でなければならない | 列挙型が許可される |
| CWG 299 | C++98 |
最初の次元の値は整数型または
列挙型でなければならない |
整数型または列挙型への単一の
変換関数を持つクラス型が 許可される |
| 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 をスローする可能性があった |
nullポインタを返すように変更 |
| CWG 2102 | C++98 |
空の配列を初期化する際に、デフォルト/値初期化が
適切な形式である必要があるかどうかが不明確であった |
必要とされる |
| CWG 2382 | C++98 |
非割り当て配置配列
new
が割り当てオーバーヘッドを必要とする可能性があった |
そのような割り当てオーバーヘッドは許可されない |
| CWG 2392 | C++11 |
最初の次元が潜在的に評価されない場合でも
プログラムが不適格となる可能性があった |
この場合は適格 |
| P1009R2 | C++11 |
配列の境界が
new
式で
推論できなかった |
推論が許可される |