21

私のコードには、事実上次のものがあります。

wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );

問題のタイプはすべて組み込みであるため、簡単なデストラクタがあります。VC ++では、上記のコードは問題なく機能します。new[]メモリを割り当てdelete[]てから解放するだけです。

C ++で受け入れられますか?それは未定義の振る舞いですか?

4

7 に答える 7

19

私の最初の考えは、それは未定義の振る舞いであるということでした。

5.3.5 / 3:「2番目の選択肢(配列の削除)では、削除するオブジェクトの動的タイプが静的タイプと異なる場合、動作は未定義です。73)

脚注73には、「これは、型void*のオブジェクトがないため、型のポインタを使用してオブジェクトを削除できないことを意味します」と書かれていますvoid

1.3.3の「動的タイプ」の定義は「最も派生したオブジェクト」に言及し、1.8 / 4の「最も派生したオブジェクト」の定義はオブジェクトについて話しているため、おそらく、この例のオブジェクトには動的タイプありません。クラスタイプの。だから私は探し続けました:

5.2.10 / 3:「[reinterpret_cast]は、元の値とは異なる表現を生成する場合と生成しない場合があります」

5.3.5 / 2: "のオペランドの値はdelete、前の配列 new-expressionから得られたポインタ値でなければなりません"。

reinterpret_castが入力されたものと同じポインター値になるかどうかはわかりません。おそらく、私がまだ見つけていない標準の他のビットによってクリアされています。このコードを「OK」と呼ぶのは、ポインターをreinterpret_castすると、結果が以前と同じ「ポインター値」になることを明確に示すものがないため、delete[]に渡すことで「ポインター値」を渡すことになります。 "new[]から。

5.2.10 / 7:「[特定のポインター型間で]キャストして元の型に戻すと元のポインター値が生成されることを除いて、そのようなポインター変換の結果は指定されていません」。

これは私には悪いニュースのように見えます-キャストが同じ値を生成するということではなく、キャストのペアが同じ値を生成するというだけです。これは、単一のキャストが異なる値を生成することを許可されていることを私に示唆していますが、それは示唆的なものであり、明示的ではありません。これは、「標準が動作を記述していない場合、動作は未定義である」というルールの通常の問題です。インデックスを使用して見つけることができる段落のいずれにも記載されていないからといって、他の場所に記載されていないという意味ではありません...

実際には、バイトを検査するためにunsigned char *にキャストしたり、memcpyを使用してPODをコピーするためにvoid *にキャストしたりできるため、エイリアスを作成するためのキャストが保証されている必要があります。実装が特定のキャストでエイリアスを作成する場合は、new[]から取得した「同じ値」を渡していると思うかもしれません。しかし、それがdelete[]に十分かどうかはまだわかりません。重要なことが欠けていると思います。

于 2010-01-26T15:41:12.860 に答える
10

delete[]間違ったデストラクタを呼び出すため、これは未定義の動作です。ただし、wchar_tcharPODであるため、専用のデストラクタはなくdelete[]、ヒープ実装を呼び出してポインタを解放するだけです。したがって、動作する可能性が最も高く、バイトが失われることはありません。しかし厳密に言えば、それはまだ定義されていません。

于 2010-01-26T15:25:18.357 に答える
5

iso14882セクション5.2.10.3:

The mapping performed by reinterpret_cast is is implementation defined

iso14882セクション5.3.5.2:

The value of the operand of delete[] shall be the pointer value which resulted from a previous array new-expression

つまり、delete[]が未定義の動作を呼び出すかどうかを実装で定義します。明確な舵取り。

于 2010-01-26T16:45:28.430 に答える
5

少なくとも私が読んだように、動的型(それが指すオブジェクトの実際の型)とは異なる静的型(ポインターの型)があります。その場合、§5.3.5/3の2番目の文が適用されます。

2番目の選択肢(配列の削除)では、削除するオブジェクトの動的タイプが静的タイプと異なる場合、動作は未定義です。

編集:あなたが明らかに望んでいるのは、オブジェクトの配列の代わりに「生の」メモリのバッファを割り当てることなので、の::operator new代わりに使用することをお勧めしnew[]ます。この場合、あなたがしていることは明確に定義されており、読者に意図を明確に示しています。

于 2010-01-26T15:42:23.363 に答える
1

wchar_tcharは両方とも組み込み型であるため、正しい割り当て解除関数()void operator delete(void* ptr)が呼び出され、呼び出すデストラクタはありません。

ただし、C ++ 03標準では、の結果reinterpret_cast<T1*>(T2*)は未定義であるとされています(セクション5.2.10.7)。

オブジェクトへのポインタは、異なるタイプのオブジェクトへのポインタに明示的に変換できます。タイプ「pointertoT1」の右辺値をタイプ「pointertoT2」(T1とT2はオブジェクトタイプであり、T2のアライメント要件はT1の要件よりも厳密ではない)に変換して元のタイプに戻すことを除いて、元のポインタ値、そのようなポインタ変換の結果は指定されていません。

wchar_t*実際のPOVからは、値が有効な値ではない実装を想像することはできませんchar*。したがって、コードはすべてのプラットフォームで問題ないはずです。標準に準拠していないだけです...

于 2010-01-27T15:23:32.677 に答える
0

delete []演算子は、内部で何らかの形式のループを使用して、配列の要素を破棄します。要素が異なるオブジェクトである場合、異なるデストラクタが使用されます。これにより、未定義の動作が発生する可能性があります。これはwcharとchar(プリミティブ型)であるため、望ましくない動作を引き起こすことはおそらくありません。

警告:読み続ける場合は、自分の責任で読んでください。未定義動作の概要。これは教育目的のみです。

例1:

同じサイズの2つのオブジェクトがあり、それらのデストラクタがすべてメモリをゼロにした場合、これもまた、望ましくない動作を引き起こすことはありません。

例2:

ただし、1つのタイプが単一の4バイトハンドルをリソースにカプセル化し、もう1つのタイプがそのような要素を2つ持つ、2つのオブジェクトがあり、後者の配列を単一のケースにキャストした場合、ハンドルの半分がリークします。配列。状況は次のようになります。

..2:[1 | 2] [1|2]無料..

ここで、「2:」は配列のサイズを表します。ダウンキャスト後、コンパイラはデータを次のように認識する削除を生成します。

..2:[1][1]無料..。

したがって、無料のものは次のようになります。

..FREE [1 | 2] FREE ..

于 2010-01-26T15:30:18.110 に答える
0

なぜreinterpret_cast<..>を使用しているのですか?純粋なC++で何かを書いている場合は、キャストを再解釈する必要はありません。あなたの場合、あなたはオブジェクトのためにメモリを割り当てていません。wchar_tにメモリを割り当てています。wchar_tの配列の代わりに文字列を使用しないのはなぜですか?

于 2010-01-26T17:20:24.963 に答える