ProgrammingC++開発者

C++における'placement new'とは何ですか?このメカニズムはどのように使用されますか?

Hintsage AIアシスタントで面接を突破

答え

placement newは、C++における特別な形のnew演算子です。指定されたアドレスに既に確保されたメモリ領域にオブジェクトを配置することを可能にします。通常、手動メモリ管理のために使用され、アロケータやオブジェクトプーリング、固定バッファへのシリアル化の基盤となります。

構文:

#include <new> void* memory = malloc(sizeof(MyClass)); MyClass* obj = new (memory) MyClass(args...); // memoryのアドレスでコンストラクタを呼び出す

ここでは、メモリは別途(たとえば、mallocやアロケータを通じて)確保され、その後にクラスのコンストラクタがこのメモリで呼び出されます。デストラクタを明示的に呼び出すことを忘れないでください:

obj->~MyClass(); free(memory);

利点:

  • 独自のアロケータを実装することが可能。
  • メモリの新たな割り当てにかかるコストが削減される。
  • 標準アロケータを呼び出さないため、メモリ管理に対する制御が得られる。

ひっかけ問題

'placement new'を使用して同じメモリブロックに複数のオブジェクトを連続して配置することは可能ですか?その結果はどうなりますか?

一般的な誤解は、以前の状態に気を配らずに新しいオブジェクトをメモリに配置できるということです。実際には、同じメモリ領域に新しいオブジェクトを古いオブジェクトの上に配置すると、デストラクタを呼び出さなかった場合、リソースリークが発生し、その後のデストラクタの呼び出しは未定義動作(UB)を引き起こします。

例:

void* place = malloc(sizeof(A)); A* a = new (place) A(); B* b = new (place) B(); // オブジェクトAは破壊されていない! UB!

このテーマに関する無知からの実際のエラーの例


物語

高負荷のシステムで、開発者はmalloc + placement newを介してオブジェクトプールを実装しましたが、プールにオブジェクトを返す際にデストラクタを呼び出すことを忘れました。その結果、リソース(オブジェクト内のシステム記述子)が解放されず、リークとサーバーの「ハング」に繋がりました。



物語

バイナリデータのシリアル化のためのプロジェクトでは、バッファとplacement newを使用してオブジェクトをその場でデコードしました。しかし、配置する前にメモリをゼロで初期化することを忘れ、初期化されていないデータを読み取ったり、POD構造体で奇妙なバグが発生しました。



物語

あるモジュールは、事前に確保されたブロック内でplacement newを使用してオブジェクトの配列を配置するように移行しました。クラス内に新しいメンバーを追加した後、非自明なデストラクタを持つため、プログラムが異常終了することになりました — クリーンアップ時に各要素のデストラクタを明示的に呼び出す必要があることを忘れていました。