web-dev-qa-db-ja.com

新しい配置と削除

ここに割り当てられたすべてのメモリを削除する正しい方法は何ですか?

  const char* charString = "Hello, World";
  void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
  Buffer* buf = new(mem) Buffer(strlen(charString));

  delete (char*)buf;

OR

  const char* charString = "Hello, World";
  void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
  Buffer* buf = new(mem) Buffer(strlen(charString));

  delete buf;

または、両方とも同じですか?

45
Vink

正しい方法は次のとおりです。

buf->~Buffer();
::operator delete(mem);

削除できるのは、delete演算子から受け取ったものnew演算子のみです。 operator new関数を直接呼び出す場合は、operator delete関数も直接呼び出す必要があり、デストラクタも手動で呼び出す必要があります。

49
bdonlan

C++には2つの異なる概念があります。

  1. 新規/削除演算子

  2. 新規/削除

演算子はメモリの割り当てと割り当て解除を行います。 new式はオブジェクトを構成します。 delete式は、オブジェクトを破棄し、演算子を呼び出すことがあります。

なぜ「時々」?表現に依存するからです。裸のグローバルnewは、最初にoperator-newを呼び出してメモリを割り当ててから、オブジェクトを構築します。グローバルdeleteはデストラクタを呼び出し、メモリの割り当てを解除します。ただし、newdeleteの他のすべてのオーバーロードは異なります。

  • オーバーロードされた新しいexpressionは、オーバーロードされた新しいoperatorを呼び出してメモリを割り当ててから、オブジェクトの構築に進みます。
  • ただし、オーバーロードされた削除式などはありません。特に、「配置削除」はありません。代わりに、デストラクタを手動で呼び出す必要があります。

オブジェクトコンストラクターが例外をスローすると一致する削除演算子が呼び出されるため、新しい/削除演算子は一致するペアでオーバーロードする必要があります。ただし、オーバーロードされたnew演算子で割り当てられたオブジェクトのデストラクタを自動的に呼び出す方法はないため、自分で行う必要があります。

最初の最も基本的な例として、placement-newoperatorを考えてください。これはvoid * operator new (size_t, void * p) throw() { return p; }の形式をとる必要があります。したがって、一致するdelete演算子は何もしないことが義務付けられています:void operator delete (void *, void *) throw() { }。使用法:

void * p = ::operator new(5); // allocate only!
T * q = new (p) T();          // construct
q->~T();                      // deconstruct: YOUR responsibility
// delete (p) q;   <-- does not exist!! It would invoke the following line:
::operator delete(p, q);      // does nothing!
::operator delete(q);         // deallocate
25
Kerrek SB

Buffer::operator deleteのようなものがないと仮定すると、delete buf;バージョンは正しく、すべての適切なクリーンアップを行います。少し安全にするには、::delete buf;と言うことができます。

言語弁護士の討論資料が続きます。

5.3.5/1

delete-expression演算子は、最も派生したオブジェクト(1.8)またはnew-expressionによって作成された配列を破棄します。

削除式:

  • ::opt deleteキャスト式
  • ::opt delete [ ]キャスト式

最初の選択肢は非配列オブジェクト用で、2番目は配列用です。 ...

5.3.5/2

...最初の選択肢(オブジェクトの削除)では、deleteのオペランドの値は、nullポインター、以前の-で作成された非配列オブジェクトへのポインターnew-expression、またはそのようなオブジェクトの基本クラスを表すサブオブジェクト(1.8)へのポインター(10節)。そうでない場合、動作は未定義です。

そのため、ポインターはnew-expressionで作成されたオブジェクトを指している必要があります。

5.3.4/1

新しい式:

  • ::opt new新しい配置opt new-type-id _new-initializer_opt
  • ::opt new新しい配置opt (type-id)new-initializeropt

新しい配置:

  • (式リスト)

したがって、「プレースメントnew」はnew-expressionとしてカウントされます。 delete-expressionを禁止するものはありません。

また、delete-expressionは、カスタム作成にもかかわらずオブジェクトをクリーンアップするために正確なことを行います。

5.3.5/6-9

delete-expressionのオペランドの値がNULLポインター値でない場合、delete-expressionはオブジェクトまたは要素のデストラクタ(存在する場合)を呼び出します削除されるアレイの。 ...

delete-expressionのオペランドの値がNULLポインター値でない場合、delete-expressiondeallocation function(3.7 .4.2)。それ以外の場合、割り当て解除関数が呼び出されるかどうかは指定されていません。 [注:割り当て解除関数は、オブジェクトのデストラクタまたは配列の一部の要素が例外をスローするかどうかに関係なく呼び出されます。 -メモの終わり]

delete-expressionのキーワードdeleteの前に単項::演算子が付いている場合、ストレージの割り当てを解除するためにグローバル割り当て解除関数が使用されます。

したがって、::delete buf;は次のものと完全に同等です。

try {
    buf->~Buffer();
} catch(...) {
    ::operator delete(mem);
    throw;
}
2
aschepler