17

と混同していますか?同様演算子new []とplacement newを通常のdelete []

X* p = static_cast<X*>(operator new[](3 * sizeof(X))); 
new(p + 0) X(); 
new(p + 1) X(); 
new(p + 2) X(); 

delete[] p; // Am I allowed to use delete[] here? Or is it undefined behavior? 

:5.3.5から

X* q = new X[3](); 

(q + 2)->~X(); 
(q + 1)->~X(); 
(q + 0)->~X(); 
operator delete[](q); 
+0

たとえ動作していても(私はこれを疑う)。それはコードを維持できないものにします。クラスXへの変更(operator newの追加など)は、少なくともテストの目的で上記のコードについて知る必要があります。このXの使用法がXの実装に強く結合することは望ましくない。 –

答えて

7

私はかなりUBを与えると確信しています。

§5.3.4/ 12によれば、新しい式の配列形式では、割り当てられたメモリ量に任意のオーバーヘッドが追加される可能性があります。配列の削除は、そこに存在すると予想される余分なメモリで何かを行うことができますが、余分なスペースを割り当てなかったためではありません。最低でも通常、少なくともoperator newから返されたと思われるアドレスに戻すために割り当てられると予想される余分なメモリの量を少なくとも補うことになりますが、余分なメモリを割り当てなかったりオフセットを適用したりしていないため、それが行われるとき、operator new[]から返されなかったoperator delete[]へのポインタを渡して、UBにつながります(実際には、返されたアドレスの先頭が技術的にUBになる前にアドレスを形成しようとしています)。

同じセク​​ションでは、余分なメモリを割り当てると、そのオーバーヘッド分だけ返されたポインタをオフセットする必要があることを示しています。 /オフセットを補正せずに新しい式から返されたポインタでoperator delete[]を呼び出すと、operator new[]とは異なるポインタでoperator delete[]が呼び出され、UBに再び与えられます。

§5.3.4/ 12は非規範的な注釈ですが、標準的な文章では矛盾するものはありません。

5

[expr.delete] n3242で:

[...]

第2の代替案(の削除 の配列)、オペランドの値が の場合、NULLポインタの値または の場合は、以前の配列new-expressionの からのポインタ値になります。 でない場合、動作は未定義です。 [...]

私は、delete[] pため、pがここにリストされていないoperator newの結果として見てフォームnew[] p(新しい表現)、または0の何かの結果であったに違いないことを意味しています最初のケースが正しいと思います。


私は第2のケースがOKであると信じています。 18.6.1.2 [new.delete.array]から :

void operator delete[](void* ptr) noexcept;

[...]

が必要です: ptrがNULLポインタでなければなりません またはその値は、 オペレータnewまたはへの前回の呼び出しによって返された値 でなければなりませんオペレータの削除 への介入によって無効にされた ではないoperator new [](std :: size_t、const std :: nothrow_t &)です。 [...]

ように長いデ/割り当て関数が一致するように(例えばdelete[] (new[3] T)(3.7.4.2で同様のテキスト[basic.stc.dynamic.deallocation]、段落3があります)よく形成される)何も悪いことは起こらない。 [それとも?私はジェリーは5.3.4 [exprの中で、約警告されているものの規範的なテキストを追跡考える


]以下を参照してください。]新しい:

新しい式は、型 のstd :: size_t型の第1引数として割り当て 機能に要求 スペースの量を通過させます。その引数は であるオブジェクトのサイズより小さい でなければなりません。オブジェクトが配列の場合は、 オブジェクトのサイズよりも大きくなる場合があります( )。 [...]同じ段落に続いて

は、実装の新しい表現が実際に配列がとるスペースよりも割り当て関数からより多くのを聞いていないことを強調する例(その非規範的)であります(割り当て解除機能で利用可能なオプションのstd::size_tパラメータを格納することを念頭に置いてください)、それらが結果にオフセットすることができます。だから、すべての賭けは配列の場合にはオフです。しかし、配列以外の場合はうまくいくようです:

auto* p = new T; 
// Still icky 
p->~T(); 
operator delete(p); 
+0

2番目のケースは、オブジェクトが破棄された後にオブジェクトを使用するため、OKではありません。 –

+0

@BenVoigtどのようなオブジェクトですか? –

+0

@ルック:私は段落を見つけました。私の答えの一番下にそれを引用しました。 –

2

私はそれが合法ではないと思います。それはこれらの方程式を意味するからです:

new-expression = allocation-function + constructor 
delete-expression = destructor + deallocation-function 

何も、それ以下はありません。しかし、スタンダードはではなく、私が知る限り、正確に言うとと言います。 new-expressionallocation-function + constructor以上一緒にしている場合があります。

new-expression = allocation-function + constructor + some-other-work 
delete-expression = destructor + deallocation-function + some-other-work 
2

彼らはUBされていない場合、彼らは次のようになります。それは、実際の方程式は、この可能性があり、かつ標準はどこにも明示的に禁止していない、です。例1では、delete[]を使用しています。このメカニズムでは、いくつのオブジェクトを破棄するかの手がかりがありません。 new[]delete[]の実装がクッキーを使用する場合、これは失敗します。例2のコードでは、アドレスqoperator delete[]に渡す正しいアドレスであると想定しています。これは、Cookieを使用する実装では当てはまりません。

+0

+1、クッキーについて考えてみると、妥当性を理解する最も簡単な方法です。明確にするために、クッキーが存在するときはコンパイラによって追加されます。 'operator new []'と 'operator delete []'関数は賢明ではありません。 – Potatoswatter

2

正しいは次のようになります。

X* p = static_cast<X*>(new char[3 * sizeof(X)]); 
// ... 
delete[] static_cast<char*>(p); 

または

X* p = static_cast<X*>(operator new[](3 * sizeof(X))); 
// ... 
operator delete[](p); 

配列の型が表現は正確に新しい表現に一致する必要があります削除します。セクション5.3.5([expr.delete])は第一の代替(削除対象)において

、削除されるオブジェクトの静的な型が異なる場合を言うので


最初の例では、UBありますその動的型から、静的型は、削除されるべきオブジェクトの動的型の基本クラスでなければならず、静的型は仮想型破棄子を持つか、または振る舞いは未定義である。 2番目の選択肢(削除配列)では、削除するオブジェクトの動的タイプが静的タイプと異なる場合、その動作は未定義です。

プログラムオブジェクトが占有するストレージを再利用することにより、または明示的にデストラクタを呼び出すことにより、任意のオブジェクトの有効期間を終了してもよい:(セクション3.9 [basic.life]が)ので


私の修正バージョンはokです非自明なデストラクタを持つクラス型のオブジェクトの場合。重大ではないデストラクタを持つクラス型のオブジェクトの場合、プログラムは、オブジェクトが占有する記憶域が再利用または解放される前にデストラクタを明示的に呼び出す必要はありません。ただし、デストラクタへの明示的な呼び出しがない場合、または削除式(5.3.5)がストレージの解放に使用されない場合、デストラクタは暗黙的に呼び出されてはならず、デストラクタによって生成される副作用に依存するプログラム の動作が不確定です。第二の例は、Xときに限り許可されていない


(これも3.9 [basic.life])ので、非自明なデストラクタを有する:

は、オブジェクトのライフタイムが開始された前なく貯蔵後にそのオブジェクトが占有するのは であり、オブジェクトの存続期間が終了した後、オブジェクトが占有した記憶装置が再利用または解放された後は、オブジェクトが格納される記憶場所を指すポインタが使用できますが、限定された方法でのみ使用できます。構築中または破壊中のオブジェクトについては、12.7を参照してください。そうでない場合、 このようなポインタは、割り当てられた記憶域(3.7.4.2)を参照し、ポインタがタイプvoid*であるかのようにポインタを使用すると、 が十分に定義されます。そのようなポインタは逆参照されるかもしれませんが、結果として得られる左辺値は、以下で説明するように、限定された の方法でのみ使用できます。

プログラムがあれば動作が未定義た:オブジェクトがあろう

  • 又は非自明なデストラクタポインタを持つクラス型のものであった削除式のオペランドとして使用される、
+0

'operator new char [](n)'は有効な構文ですか? – fredoverflow

+0

@Fred:いいえ、もちろんありません。私は関数 'operator new []'を呼んでいたという質問を見落としました。私はそれが 'new []'の式であると思っていました。 –

+0

'operator delete'は' delete-expression'ではなく、解放関数です。 –

関連する問題