次のコードがあるとします。
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
これは安全ですか?または、削除ptr
する前にキャストする必要がありますか?char*
次のコードがあるとします。
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
これは安全ですか?または、削除ptr
する前にキャストする必要がありますか?char*
voidポインタを介した削除は、C++標準では定義されていません。セクション5.3.5/3を参照してください。
最初の選択肢(オブジェクトの削除)では、オペランドの静的型が動的型と異なる場合、静的型はオペランドの動的型の基本クラスであり、静的型は仮想デストラクタを持っているか、動作が未定義です。 。2番目の選択肢(配列の削除)では、削除するオブジェクトの動的タイプが静的タイプと異なる場合、動作は未定義です。
そしてその脚注:
これは、void型のオブジェクトがないため、void*型のポインタを使用してオブジェクトを削除できないことを意味します。
。
これは良い考えではなく、C++ で行うようなことでもありません。理由もなく型情報を失っています。
非プリミティブ型に対して呼び出すと、削除する配列内のオブジェクトに対してデストラクタは呼び出されません。
代わりに、new/delete をオーバーライドする必要があります。
void* を削除すると、たまたまメモリが正しく解放される可能性がありますが、結果が定義されていないため、間違っています。
なんらかの理由でポインタを void* に格納してから解放する必要がある場合は、malloc と free を使用する必要があります。
それは「安全」に依存します。割り当て自体に関するポインタとともに情報が格納されるため、通常は機能します。これにより、デロケータはそれを適切な場所に戻すことができます。この意味で、アロケータが内部境界タグを使用している限り、「安全」です。(多くの場合)
ただし、他の回答で述べたように、voidポインタを削除してもデストラクタは呼び出されないため、問題が発生する可能性があります。その意味で、「安全」ではありません。
あなたがしているようにあなたがしていることをする正当な理由はありません。独自の割り当て解除関数を作成する場合は、関数テンプレートを使用して、正しいタイプの関数を生成できます。そのための適切な理由は、特定のタイプに対して非常に効率的なプールアロケータを生成することです。
他の回答で述べたように、これはC++では未定義の動作です。トピック自体は複雑で相反する意見でいっぱいですが、一般的には未定義の動作を避けるのが良いでしょう。
デストラクタは実際に指している値に対して呼び出されないため、void ポインタの削除は危険です。これにより、アプリケーションでメモリ/リソース リークが発生する可能性があります。
多くの人が、いいえ、void ポインターを削除するのは安全ではないとコメントしています。私はそれに同意しますが、連続した配列または同様のものを割り当てるために void ポインターを使用している場合は、これを使用して安全new
に使用できるようにすることも追加したいと思いますdelete
(with、ahem 、少し余分な作業)。これは、メモリ領域 (「アリーナ」と呼ばれる) に void ポインターを割り当て、アリーナへのポインターを new に提供することによって行われます。C++ FAQのこのセクションを参照してください。これは、C++ でメモリ プールを実装する一般的な方法です。
void* を使用したい場合は、malloc/free だけを使用してください。new/delete は単なるメモリ管理ではありません。基本的に、新規/削除はコンストラクタ/デストラクタを呼び出し、さらに多くのことが行われます。組み込み型 (char* など) を使用し、それらを void* で削除するだけでも機能しますが、推奨されません。要するに、void* を使用する場合は malloc/free を使用することです。それ以外の場合は、便宜上テンプレート関数を使用できます。
template<typename T>
T* my_alloc (size_t size)
{
return new T [size];
}
template<typename T>
void my_free (T* ptr)
{
delete [] ptr;
}
int main(void)
{
char* pChar = my_alloc<char>(10);
my_free(pChar);
}
char には特別なデストラクタ ロジックがないためです。これはうまくいきません。
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
医者は呼ばれません。
これを行う理由はほとんどありません。
まず第一に、データの型がわからずvoid*
、それが であるということだけがわかっている場合、そのデータを型のないバイナリ データのブロブ( unsigned char*
)として扱い、 malloc
/free
を使用してそれを処理する必要があります。 . void*
これは、C API へのポインターを渡す必要がある波形データなどで必要になることがあります。それはいいです。
データの型がわかっている (つまり、ctor/dtor がある) が、何らかの理由でvoid*
(なんらかの理由で) ポインターになってしまった場合は、それを知っている型にキャストし直す必要があります。であり、それを呼び出しますdelete
。
char の特定のケースについて。
char は、特別なデストラクタを持たない組み込み型です。したがって、リークの議論は議論の余地があります。
sizeof(char) は通常 1 であるため、配置引数もありません。sizeof(char) が 1 でないまれなプラットフォームの場合、char に対して十分に整列されたメモリを割り当てます。したがって、アラインメントの議論も議論の余地があります。
この場合、malloc/free の方が高速です。ただし、std::bad_alloc を放棄し、malloc の結果を確認する必要があります。仲介者をバイパスするため、グローバルな new および delete 演算子を呼び出す方がよい場合があります。
私はコード リフレクションやその他のあいまいな作業のためにフレームワークで void* (不明な型とも呼ばれます) を使用してきましたが、これまでのところ、どのコンパイラからも問題 (メモリ リーク、アクセス違反など) は発生していません。操作が非標準であるため、警告のみ。
不明 (void*) を削除することは完全に理にかなっています。ポインターが次のガイドラインに従っていることを確認してください。
1) 不明なポインターは、自明なデコンストラクターを持つ型を指してはならないため、不明なポインターとしてキャストされた場合は、決して削除しないでください。未知のポインタを元の型にキャストした後にのみ削除してください。
2) インスタンスは、スタック バウンドまたはヒープ バウンド メモリ内の不明なポインタとして参照されていますか? 不明なポインターがスタック上のインスタンスを参照している場合は、決して削除しないでください。
3) 未知のポインタが有効なメモリ領域であることに 100% 確信がありますか? いいえ、決して削除しないでください。
全体として、不明な (void*) ポインター型を使用して実行できる直接的な作業はほとんどありません。ただし、間接的には、void* は、C++ 開発者がデータのあいまいさが必要な場合に頼れる優れた資産です。