C++ プログラミング言語
では、newとdeleteは動的なメモリ割り当て、オブジェクトの構築、オブジェクトの破棄を実行する言語構成要素のペアです。[1]
概要
「配置new」と呼ばれる形式を除き、new演算子はプロセスのヒープ上のメモリ割り当ての要求を表します。十分なメモリが利用可能な場合、newはメモリを初期化し、必要に応じてオブジェクトコンストラクタを呼び出し、新しく割り当てられ初期化されたメモリのアドレスを返します。[2] [3]最も単純な形式のnew要求は次のようになります。
p =新しいT ;
ここで、p は、以前に宣言されたT型のポインタ(またはTのスーパークラスなど、 Tポインタを割り当てることができる他の型)です。 Tのデフォルト コンストラクターがある場合は、それが呼び出され、割り当てられたメモリ バッファーに Tインスタンスが構築されます。
T型のオブジェクトに対して空きストアに十分なメモリがない場合、新しい要求はstd::bad_alloc型の例外をスローして失敗を示します。これにより、割り当ての結果を明示的にチェックする必要がなくなります。
newの割り当て解除に相当するのがdeleteです。これは最初に引数のデストラクタ (存在する場合) を呼び出し、次にnewによって割り当てられたメモリをフリー ストアに戻します。new の呼び出しはすべてdeleteの呼び出しと一致する必要があります。そうしないとメモリ リークが発生します。[1]
新しい構文には、メモリ割り当てとオブジェクト構築をより細かく制御できるいくつかのバリエーションがあります。関数呼び出しのような構文は、デフォルトのものとは異なるコンストラクタを呼び出して引数を渡すために使用されます。例:
p =新しいT (引数);
新しく割り当てられたバッファを初期化するときに、デフォルトのコンストラクターではなく、 単一引数のTコンストラクターを呼び出します。
別のバリアントでは、単一のオブジェクトではなくオブジェクトの 配列を割り当てて初期化します 。
p =新しいT [ N ];
これは、 T型のN 個のオブジェクトの連続した配列を保持するのに十分な大きさのメモリ バッファーをフリー ストアから要求し、配列の各要素に対して既定のコンストラクターを呼び出します。
new[]で割り当てられたメモリは、 deleteではなくdelete[]演算子で解放する必要があります。不適切な形式を使用すると、未定義の動作が発生します。C++ コンパイラは、間違った形式を使用した場合に診断メッセージを生成する必要はありません。
C ++11標準では追加の構文が規定されており、
p =新しいT [ N ] {初期化子1 、...、初期化子N };
各p[ i ] を初期化子i+1に初期化します。
エラー処理
new が割り当て要求を処理するのに十分なメモリを見つけられない場合、3 つの異なる方法でエラーを報告できます。まず、ISO C++ 標準では、プログラムがC++ランタイムにnew_handlerと呼ばれるカスタム関数を登録できます。登録すると、new がエラーに遭遇するたびにこの関数が呼び出されます。new_handler は、より多くのメモリを使用できるようにしようとしたり、それができない場合はプログラムを終了したりします。
new_handlerがインストールされていない場合、new は代わりにstd::bad_alloc型の例外をスローします。したがって、プログラムはCの習慣のように返されたポインターの値をチェックする必要がありません。例外がスローされなかった場合、割り当ては成功しています。
エラー処理の 3 番目の方法は、例外をスローしないことを指定するバリアント形式new(std::nothrow)によって提供され、代わりに割り当てエラーを通知するために null ポインターが返されます。
オーバーロード
new演算子をオーバーロードすると、特定の型 (クラス) がインスタンスにカスタム メモリ割り当てアルゴリズムを使用するようにできます。たとえば、次の例はシングルトン パターンのバリエーションであり、最初のnew Singleton呼び出しでインスタンスが割り当てられ、後続のすべての呼び出しで同じインスタンスが返されます。
#include <cstdlib>
#include <cstddef>
クラスシングルトン{
公共:
静的void *演算子new (std :: size_tサイズ){
if ( !インスタンス) {
インスタンス= std :: malloc (サイズ);
}
参照カウント++ ;
インスタンスを返します。
}
静的void演算子delete (void * )noexcept {
if ( --refcount == 0 ) {
std :: free (インスタンス);
インスタンス= nullptr ;
}
}
プライベート:
静的void *インスタンス= nullptr ;
静的std :: size_trefcount = 0 ;
};
この機能は C++ の歴史の初期から利用可能でしたが、特定のオーバーロード メカニズムは変更されました。オブジェクト指向のC++ プログラムでは、内部的に C アロケータ (「malloc と free との関係」を参照) を使用するnewで多くの小さなオブジェクトを割り当てる傾向があるため、この機能が言語に追加されました。ただし、これは一般的な C プログラムで実行されるより少ない、より大きな割り当てに最適化されていました。Stroustrupは、初期のアプリケーションでは、C 関数mallocが「実際のシステムで最も一般的なパフォーマンスのボトルネック」であり、プログラムが最大 50% の時間をこの関数に費やしていたと報告しました。[4]
mallocとfreeとの関係
標準 C++ はC 標準ライブラリを包含しているため、C の動的メモリ割り当てルーチンmalloc、calloc、realloc、freeも C++ プログラマーが利用できます。これらのルーチンはオブジェクトの初期化と破棄を行わないため、ほとんどの場合、使用は推奨されません。[5]実際、 newとdelete は、手動でオブジェクトを初期化する必要を回避するために、C++ の最初のバージョン (当時は「C with Classes 」と呼ばれていました) で導入されました。 [4]
reallocを使用して割り当てられた配列を拡大または縮小できる C ルーチンとは対照的に、 new[]によって割り当てられたメモリ バッファーのサイズを変更することはできません。代わりに、 C++ 標準ライブラリは、std::vectorテンプレート クラス で拡張または縮小できる動的配列(コレクション)を提供します。
C++標準では、 new / deleteとCのメモリ割り当てルーチンとの関係は規定されていないが、 newとdeleteは通常、 mallocとfreeのラッパーとして実装されている。[6] 2つの操作ファミリを混在させると、例えば新しく割り当てられたメモリをfreeしたり、 mallocされたメモリをdeleteしたりすると、未定義の動作が発生し、実際にはロックの解放に失敗してデッドロックが発生するなど、さまざまな壊滅的な結果につながる可能性がある。[7]
参照
参考文献
- ^ ab Savitch, Walter (2013). Absolute C++ . ピアソン. pp. 420–445. ISBN 978-0132846813。
- ^ 「C++ の演算子 new を説明する IBM ドキュメント」。2013 年 1 月 3 日時点のオリジナルよりアーカイブ。2013年 11 月 6 日閲覧。
- ^ 「Microsoft Visual Studio オペレーターの新しいドキュメント」 。2013年 11 月 6 日閲覧。
- ^ ab Stroustrup, Bjarne (1993). C++ の歴史: 1979–1991 (PDF) . Proc. ACM History of Programming Languages Conf.
- ^ マイヤーズ、スコット(1998)。Effective C++。Addison-Wesley。p. 21。ISBN 9780201924886。
- ^ Alexandrescu, Andrei (2001). Modern C++ Design: Generic Programming and Design Patterns Applied . Addison-Wesley. p. 68.
- ^ Seacord, Robert C. (2013). C および C++ でのセキュアコーディング。Addison-Wesley。セクション 4.4、一般的な C++ メモリ管理エラー。
