17

好奇心からですが、次は合法ですか?

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?

同様に:

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

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

5 に答える 5

7

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

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

同じセクションでは、追加のメモリを割り当てる場合は、返されたポインタをそのオーバーヘッドの量だけオフセットする必要があると述べています。operator delete[]オフセットを補正せずに新しい式から返されたポインターを使用して呼び出す場合、返されoperator delete[]たポインターとは異なるポインターを使用して呼び出しoperator new[]、UBを再度提供します。

§5.3.4/12は非規範的なメモですが、規範的なテキストにはそれと矛盾するものは何もありません。

于 2011-06-27T16:26:16.533 に答える
5

n3242 の 5.3.5 [expr.delete] から:

2

[...]

2 番目の選択肢 ( delete array ) では、delete のオペランドの値は、null ポインター値または前の配列 new-expression から得られたポインター値である可能性があります。そうでない場合、動作は未定義です。[...]

つまり、 forは何かの形式(新しい式) または 0の結果delete[] pである必要があります。 の結果がここにリストされていないので、最初のケースは正しいと思います。pnew[] poperator new


2番目のケースは大丈夫だと思います。18.6.1.2 [new.delete.array] から:

11

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

[...]

必須: ptr は null ポインターであるか、その値は、以前の operator new または operator new[](std::size_t,const std::nothrow_t&) への呼び出しによって無効化されていない呼び出しによって返された値でなければなりません。オペレーター削除。[...]

(3.7.4.2 [basic.stc.dynamic.deallocation] の第 3 段落にも同様のテキストがあります)

したがって、de/allocation 関数が一致する限り (たとえばdelete[] (new[3] T)整形式である場合)、悪いことは何も起こりません。[またはそれはありますか?下記参照 ]


5.3.4 [expr.new] で、ジェリーが警告していることの規範的なテキストを追跡したと思います。

10

new 式は、要求されたスペースの量を std::size_t 型の最初の引数として割り当て関数に渡します。その引数は、作成されるオブジェクトのサイズ以上でなければなりません。オブジェクトが配列の場合にのみ、作成されるオブジェクトのサイズよりも大きくなることがあります。[...]

同じ段落に続くのは、実装の新しい式が実際に配列が占めるスペースよりも多くを割り当て関数から自由に要求できることを強調する例です (非規範的) (std::size_t割り当て解除関数で使用可能なオプションのパラメーターを格納する心)、結果にオフセットできること。したがって、アレイの場合はすべての賭けが無効になります。ただし、非配列の場合は問題ないようです。

auto* p = new T;
// Still icky
p->~T();
operator delete(p);
于 2011-06-27T16:26:03.243 に答える
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);

配列の削除式の型は、新しい式と正確に一致する必要があります。


最初の例は UB です。セクション 5.3.5 ( [expr.delete])で

最初の選択肢 (オブジェクトの削除) では、削除するオブジェクトの静的型がその動的型と異なる場合、静的型は削除するオブジェクトの動的型の基本クラスであり、静的型は仮想デストラクタまたは動作は未定義です。2 番目の選択肢 ( delete array ) では、削除するオブジェクトの動的な型がその静的な型と異なる場合、動作は未定義です。


(セクション3.9 [basic.life]):

プログラムは、オブジェクトが占有するストレージを再利用するか、非自明なデストラクタを使用してクラス型のオブジェクトのデストラクタを明示的に呼び出すことにより、オブジェクトの有効期間を終了できます。自明でないデストラクタを持つクラス型のオブジェクトの場合、プログラムは、オブジェクトが占有するストレージが再利用または解放される前に、デストラクタを明示的に呼び出す必要はありません。ただし、デストラクタへの明示的な呼び出しがない場合、またはストレージを解放するために削除式 (5.3.5) が使用されていない場合、デストラクタは暗黙的に呼び出されてはならず、デストラクタによって生成される副作用に依存するすべてのプログラム未定義の動作があります。


2 番目の例はX、自明でないデストラクタがある場合は許可されません (3.9 も[basic.life]):

オブジェクトの存続期間が開始する前で、オブジェクトが占有するストレージが割り当てられた後 38、またはオブジェクトの存続期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、オブジェクトを参照するすべてのポインタオブジェクトが配置される、または配置された保管場所は使用できますが、限られた方法でのみ使用できます。建設中または破壊中のオブジェクトについては、12.7 を参照してください。それ以外の場合、そのようなポインターは割り当てられたストレージ (3.7.4.2) を参照し、ポインターが型void*であるかのようにポインターを使用することは明確に定義されています。このようなポインターは参照解除できますが、結果の左辺値は、以下で説明するように、限られた方法でのみ使用できます。

次の場合、プログラムは未定義の動作をします。

  • オブジェクトが非自明なデストラクタを持つクラス型であるか、そうであり、ポインターが削除式のオペランドとして使用されている場合、
于 2011-06-27T16:34:36.457 に答える
2

法律的にありえないと思います。これは次の方程式を意味するためです。

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

それ以上でもそれ以下でもありません。しかし、私が知る限り、規格は正確にはそうは言っていません。一緒に行うnew-expressionよりも多くのことを行う可能性があります。allocation-function + constructorつまり、実際の方程式は次のようになる可能性があり、標準では明示的に禁止されていません。

new-expression    = allocation-function  +  constructor   +  some-other-work
delete-expression = destructor  +  deallocation-function  +  some-other-work
于 2011-06-27T16:29:13.547 に答える
2

UB でない場合は、UB にする必要があります。例 1 ではdelete[]、基になるメカニズムが破壊されるオブジェクトの数の手がかりがない場所を使用しています。実装でCookienew[]を使用している場合、これは失敗します。例 2 のコードは、アドレスが に渡す正しいアドレスであるdelete[]と想定していますが、これは Cookie を使用する実装には当てはまりません。qoperator delete[]

于 2011-06-27T16:31:33.623 に答える