キーワード new および delete のさまざまな使用法は、かなりの混乱を引き起こしているようです。C++ で動的オブジェクトを構築するには、常に 2 つの段階があります。生メモリの割り当てと、割り当てられたメモリ領域での新しいオブジェクトの構築です。オブジェクトの有効期間の反対側には、オブジェクトの破棄と、オブジェクトが存在するメモリ位置の割り当て解除があります。
多くの場合、これら 2 つの手順は単一の C++ ステートメントによって実行されます。
MyObject* ObjPtr = new MyObject;
//...
delete MyObject;
上記の代わりに、C++ の raw メモリ割り当て関数operator new
とoperator delete
、(placement を介しnew
た) 明示的な構築と破棄を使用して、同等の手順を実行できます。
void* MemoryPtr = ::operator new( sizeof(MyObject) );
MyObject* ObjPtr = new (MemoryPtr) MyObject;
// ...
ObjPtr->~MyObject();
::operator delete( MemoryPtr );
キャストが含まれておらず、割り当てられたメモリ領域に 1 種類のオブジェクトのみが構築されていることに注目してください。生メモリを割り当てる方法としてのようなものを使用することnew char[N]
は、論理的にchar
は、新しく割り当てられたメモリにオブジェクトが作成されるため、技術的に正しくありません。「うまくいかない」状況については知りませんが、生のメモリ割り当てとオブジェクト作成の区別が曖昧になるため、使用しないことをお勧めします。
この特定のケースでは、 の 2 つのステップを分離してもメリットdelete
はありませんが、初期割り当てを手動で制御する必要があります。MyObject
上記のコードは「すべてが機能している」シナリオで機能しますが、のコンストラクターが例外をスローした場合、生のメモリがリークします。これは、割り当ての時点で例外ハンドラーを使用してキャッチおよび解決できますが、配置 new 式によって完全な構築を処理できるように、カスタム演算子 new を提供する方がおそらく適切です。
class MyObject
{
void* operator new( std::size_t rqsize, std::size_t padding )
{
return ::operator new( rqsize + padding );
}
// Usual (non-placement) delete
// We need to define this as our placement operator delete
// function happens to have one of the allowed signatures for
// a non-placement operator delete
void operator delete( void* p )
{
::operator delete( p );
}
// Placement operator delete
void operator delete( void* p, std::size_t )
{
::operator delete( p );
}
};
ここにはいくつかの微妙な点があります。クラス配置 new を定義して、クラス インスタンスに十分なメモリとユーザー指定可能なパディングを割り当てることができるようにします。これを行うため、一致する配置の削除を提供する必要があります。これにより、メモリの割り当てが成功しても構築が失敗した場合、割り当てられたメモリが自動的に割り当て解除されます。残念ながら、プレースメント削除の署名は、プレースメント以外の削除で許可されている 2 つの署名のいずれかと一致するため、実際のプレースメント削除がプレースメント削除として扱われるように、別の形式の非プレースメント削除を提供する必要があります。(placement new とplacement delete の両方に追加のダミー パラメータを追加することでこれを回避できますが、これにはすべての呼び出しサイトで余分な作業が必要になります。)
// Called in one step like so:
MyObject* ObjectPtr = new (padding) MyObject;
単一の new 式を使用すると、 new 式の一部がスローされてもメモリがリークしないことが保証されます。
オブジェクトの有効期間の反対側では、delete 演算子を定義したため (定義していなかったとしても、オブジェクトのメモリはもともとグローバル演算子 new から取得されたものでした)、動的に作成されたオブジェクトを破棄する正しい方法は次のとおりです。 .
delete ObjectPtr;
概要!
キャストを見てください!operator new
raw メモリをoperator delete
扱うと、placement new は raw メモリ内にオブジェクトを構築できます。a からオブジェクト ポインターへの明示的なキャストvoid*
は、通常、「正常に機能する」場合でも、論理的に何かが間違っていることを示しています。
new[] と delete[] を完全に無視しました。これらの可変サイズのオブジェクトは、どのような場合でも配列では機能しません。
new の配置により、新しい式がリークしないようにすることができます。新しい式は、破棄が必要なオブジェクトと割り当て解除が必要なメモリへのポインターとして評価されます。何らかの種類のスマート ポインターを使用すると、他の種類のリークを防ぐことができる場合があります。delete
プラス面として、これを行う正しい方法としてプレーンを使用できるようにしたため、ほとんどの標準的なスマート ポインターが機能します。