ここに割り当てられたすべてのメモリを削除する正しい方法は何ですか?
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;
または、両方とも同じですか?
正しい方法は次のとおりです。
buf->~Buffer();
::operator delete(mem);
削除できるのは、delete
演算子から受け取ったものnew
演算子のみです。 operator new
関数を直接呼び出す場合は、operator delete
関数も直接呼び出す必要があり、デストラクタも手動で呼び出す必要があります。
C++には2つの異なる概念があります。
新規/削除演算子。
新規/削除式。
演算子はメモリの割り当てと割り当て解除を行います。 new
式はオブジェクトを構成します。 delete
式は、オブジェクトを破棄し、演算子を呼び出すことがあります。
なぜ「時々」?表現に依存するからです。裸のグローバルnew
は、最初にoperator-newを呼び出してメモリを割り当ててから、オブジェクトを構築します。グローバルdelete
はデストラクタを呼び出し、メモリの割り当てを解除します。ただし、new
とdelete
の他のすべてのオーバーロードは異なります。
オブジェクトコンストラクターが例外をスローすると一致する削除演算子が呼び出されるため、新しい/削除演算子は一致するペアでオーバーロードする必要があります。ただし、オーバーロードされた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
Buffer::operator delete
のようなものがないと仮定すると、delete buf;
バージョンは正しく、すべての適切なクリーンアップを行います。少し安全にするには、::delete buf;
と言うことができます。
言語弁護士の討論資料が続きます。
5.3.5/1
delete-expression演算子は、最も派生したオブジェクト(1.8)またはnew-expressionによって作成された配列を破棄します。
削除式:
::
optdelete
キャスト式::
optdelete [ ]
キャスト式最初の選択肢は非配列オブジェクト用で、2番目は配列用です。 ...
5.3.5/2
...最初の選択肢(オブジェクトの削除)では、
delete
のオペランドの値は、nullポインター、以前の-で作成された非配列オブジェクトへのポインターnew-expression、またはそのようなオブジェクトの基本クラスを表すサブオブジェクト(1.8)へのポインター(10節)。そうでない場合、動作は未定義です。
そのため、ポインターはnew-expressionで作成されたオブジェクトを指している必要があります。
5.3.4/1
新しい式:
::
optnew
新しい配置opt new-type-id _new-initializer_opt::
optnew
新しい配置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-expressionはdeallocation function(3.7 .4.2)。それ以外の場合、割り当て解除関数が呼び出されるかどうかは指定されていません。 [注:割り当て解除関数は、オブジェクトのデストラクタまたは配列の一部の要素が例外をスローするかどうかに関係なく呼び出されます。 -メモの終わり]
delete-expressionのキーワード
delete
の前に単項::
演算子が付いている場合、ストレージの割り当てを解除するためにグローバル割り当て解除関数が使用されます。
したがって、::delete buf;
は次のものと完全に同等です。
try {
buf->~Buffer();
} catch(...) {
::operator delete(mem);
throw;
}