名前空間
変種
操作

new 式

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
new
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

動的記憶域期間を持つオブジェクト、つまり、生存期間が作成されたスコープに制限されないオブジェクトを、作成して初期化します。

目次

[編集] 構文

::(オプション) new (placement_params)(オプション) ( type ) initializer(オプション) (1)
::(オプション) new (placement_params)(オプション) type initializer(オプション) (2)
1) 型識別子 type によって表される型のオブジェクトの作成を試みます。 型は配列型でも構いませんし、プレースホルダ型指定子を含んでも構いません (C++11以上)し、クラステンプレートの実引数推定によって推定される引数を持つクラステンプレートを含んでも構いません (C++17以上)
2) 同じですが、 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 です (コンパイル時エラーが発行されます)。
  • そうでなく、呼ばれるであろう確保関数が例外を投げない場合、 new 式は要求される結果の型のヌルポインタを返します。
(C++14以上)
  • そうでなければ、 new 式は確保関数を呼ばず、代わりに std::bad_array_new_length 型またはそこから派生した型の例外を投げます。
(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 式が charunsigned 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 クラスのメンバ関数によってカプセル化されています。

__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超えるアライメント要件を持つオブジェクト、またはそのようなオブジェクトの配列を確保するとき、 new 式は (std::align_val_t にラップされた) アライメント要件を確保関数に対する第2引数として渡します (配置形式の場合、 placement_params はアライメントの後 (第3引数から) に現れます)。 オーバーロード解決に失敗した場合 (クラス固有の確保関数が異なるシグネチャで定義されているときに発生します (グローバルなものを隠蔽するため))、引数リストにアライメントなしで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 式は直ちに戻り、オブジェクトの初期化や解放関数の呼び出しを試みません。 標準の配置確保関数がヌルポインタを返した場合 (引数としてヌルポインタを渡した場合に起き得ます)、動作は未定義です。 (C++17以上)

[編集] 構築

new 式によって作成されたオブジェクトは以下のルールに従って初期化されます。

  • 非配列の type の場合、取得されたメモリ領域に単一のオブジェクトが構築されます。
  • initializer がない場合、オブジェクトはデフォルト初期化されます。
  • initializer が括弧で囲まれた引数リストの場合、オブジェクトは直接初期化されます。
  • initializer が波括弧で囲まれた引数リストの場合、オブジェクトはリスト初期化されます。
(C++11以上)
  • type が配列型の場合、オブジェクトの配列が初期化されます。
  • initializer がない場合、それぞれの要素はデフォルト初期化されます。
  • initializer が空の括弧の場合、それぞれの要素は値初期化されます。
  • initializer が波括弧で囲まれた引数リストの場合、配列は集成体初期化されます。
(C++11以上)
  • initializer が丸括弧で囲まれた引数リストの場合、配列は集成対初期化されます。
(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 式が実行されることを保証します。

[編集] キーワード

new

[編集] 欠陥報告

以下の動作変更欠陥報告は以前に発行された 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