C++でのメモリの割り当て解除について質問があります。
typedef struct type1
{
int a;
int b;
float c;
} Type1;
typedef struct type2
{
int a;
int b;
} Type2;
void *p = new Type1;
delete (Type2 *)p;
この場合、別のサイズのタイプにキャストしたp
後でも、が指しているメモリ領域は完全に削除されますか?p
動作は未定義です。この場合、動的に割り当てられたオブジェクトは、 type のポインタを介してのみ削除できますType1
。
まず、 によって取得さ(Type2 *)p
れたポインタをdelete
式で使用すると、エイリアシング ルールに違反します。指し示すオブジェクトをp
使用できる型のセットは限られています。C++03 の規則は、別の質問への回答に記載されている場合があります。C++11 のルールは似ています (違いは質問への回答とは関係ありません)。
プログラムが厳密なエイリアシング規則に違反していなくても、delete
式の要件に違反することになります。仕様には次のように記載されています (C++11 §5.3.5[expr.delete]/3):
削除するオブジェクトの静的型がその動的型と異なる場合、静的型は削除するオブジェクトの動的型の基本クラスであり、静的型は仮想デストラクタを持つ必要があります。そうしないと、動作が未定義になります。
式では、delete
オブジェクトの静的タイプは ですがType2
、動的タイプはType1
です。型は異なりますが、静的型は動的型の基本クラスではありません。
Type1 ポインターで Type2::~Type2 を実行するようにコンパイラーに依頼することになり、そのデストラクタがオブジェクトの末尾を参照する可能性があるため、これは非常に悪い考えです。
従来の環境では、コンパイル時に呼び出された型を気にしないoperator delete
呼び出しが行われるため、メモリの最終的な解放は問題ありません。free
ただし、それほど一般的ではない環境では、災害になる可能性があります。
この質問はすでにJamesによって完全に回答されていますが、1 つ指摘したいことがあります。
適切な C++ コードでは、ポインターを操作することはほとんどありません。void
つまり、コードはおそらく次のようになります。
SubType *p = new SubType;
BaseType* pB = (BaseType*)p;
delete pB;
この場合、 にBaseType
適切な仮想コンストラクターがあったとしても、 がSubType
から派生していない場合、未定義の動作が発生する可能性がありBaseType
ます。一般的な C スタイルのキャストは、ここではあまり幸運な選択ではありません。
しかし、 を使用する場合、それがポリモーフィック型のオブジェクトを指しdynamic_cast
ていない場合、コンパイラはおそらくそれを許可しません。p
そしてp
、それがポリモーフィック型のオブジェクトを指しているが、BaseType
の基本型ではないSubType
場合でも、dynamic_cast
返さNULL
れ、この状態を適切に処理できます。
SubType *p = new SubType;
BaseType* safePtr = dynamic_cast<BaseType *>(p);
if (!safePtr) // p doesn't point to object of type derived from BaseType
... // handle this situation
else // p points to object of polymorphic type derived from BaseType
delete safePtr;