次のコードがあるとします。
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 ++での未定義の動作です。一般に、トピック自体は複雑で意見の対立で満たされていますが、未定義の動作を回避することをお勧めします。
sizeof(T*) == sizeof(U*)
すべての人T,U
が、テンプレート化されていない、void *
ベースのガベージコレクター実装を1つ持つことが可能であることを示唆しているという事実。しかし、その後、GCが実際にポインタを削除/解放する必要がある場合、まさにこの質問が発生します。これを機能させるには、ラムダ関数デストラクタラッパー(urgh)が必要か、または型と格納可能な型の間を行き来できる、ある種の動的な「データとしての型」が必要です。
voidポインタによる削除は、C ++標準では定義されていません。セクション5.3.5 / 3を参照してください。
最初の代替方法(オブジェクトの削除)では、オペランドの静的タイプが動的タイプと異なる場合、静的タイプはオペランドの動的タイプの基本クラスであり、静的タイプは仮想デストラクタを持つか、動作が未定義です。2番目の方法(配列の削除)では、削除するオブジェクトの動的タイプが静的タイプと異なる場合、動作は未定義です。
そしてその脚注:
これは、void型のオブジェクトがないため、void *型のポインターを使用してオブジェクトを削除できないことを意味します。
。
NULL
をアプリケーションメモリ管理に何らかの違いを持たせて埋めますか?
これは良い考えではなく、C ++で行うことでもありません。理由もなくタイプ情報を失っています。
デストラクタは、プリミティブ以外のタイプで呼び出すときに削除する配列内のオブジェクトでは呼び出されません。
代わりに、new / deleteをオーバーライドする必要があります。
void *を削除するとメモリが正しく解放される可能性がありますが、結果が定義されていないため、誤りです。
何らかの理由で不明な点があり、ポインタをvoid *に格納してから解放する必要がある場合は、mallocとfreeを使用する必要があります。
voidポインタを削除すると、デストラクタが実際に指す値で呼び出されなくなるため、危険です。これにより、アプリケーションでメモリ/リソースリークが発生する可能性があります。
その質問は意味がありません。あなたの混乱は、人々がよく使うずさんな言語が原因の一部かもしれませんdelete
:
動的に割り当てられdelete
たオブジェクトを破棄するために使用します。そのためには、そのオブジェクトへのポインターを使用して削除式を作成します。「ポインタを削除する」ことは決してありません。実際に行うことは、「アドレスで識別されるオブジェクトを削除する」ことです。
質問が意味をなさない理由がわかります:voidポインターは「オブジェクトのアドレス」ではありません。これは単なるアドレスであり、セマンティクスはありません。実際のオブジェクトのアドレスから取得された可能性がありますが、その情報は元のポインターのタイプでエンコードされているため、失われます。オブジェクトポインターを復元する唯一の方法は、voidポインターをオブジェクトポインターにキャストすることです(これには、作成者がポインターの意味を知っている必要があります)。void
それ自体は不完全な型であり、したがってオブジェクトの型になることはありません。また、voidポインターを使用してオブジェクトを識別することもできません。(オブジェクトは、タイプとアドレスによって共同で識別されます。)
delete
nullポインター値、以前のnew-expressionによって作成された非配列オブジェクトへのポインター、またはそのようなオブジェクトの基本クラスを表すサブオブジェクト。そうでない場合、動作は未定義です。」したがって、コンパイラが診断なしでコードを受け入れる場合、それはコンパイラのバグにすぎません...
delete void_pointer
。これは未定義の動作です。プログラマーが望んでいたことを応答が実行しているように見えても、プログラマーは未定義の動作を呼び出すべきではありません。
本当にこれを行う必要がある場合は、仲介者(new
およびdelete
演算子)を切り取り、グローバルoperator new
でoperator delete
直接呼び出しませんか?(もちろん、new
and delete
演算子を計測しようとしている場合は、実際にoperator new
and を再実装する必要がありoperator delete
ます。)
void* my_alloc (size_t size)
{
return ::operator new(size);
}
void my_free (void* ptr)
{
::operator delete(ptr);
}
とは異なりmalloc()
、失敗時にoperator new
スローstd::bad_alloc
します(または、new_handler
登録されている場合はを呼び出します)。
charには特別なデストラクタロジックがないためです。これは機能しません。
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
d'ctorは呼び出されません。
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);
}
これを行う理由はほとんどありません。
まず、データのタイプがわからずvoid*
、それがであることがわかっている場合、実際には、そのデータをタイプなしのバイナリデータのブロブ(unsigned char*
)として扱い、malloc
/ free
を使用して処理する必要があります。 。これはvoid*
、C apiへのポインタを渡す必要がある波形データなどの場合に必要になることがあります。それはいいです。
あなたがいる場合行うデータの種類を(すなわち、それはCTOR /デストラクタを持っている)知っているが、何らかの理由であなたが終わったvoid*
(何らかの理由であなたが持っている)、ポインタ、あなたが本当に型に戻ってそれをキャストする必要があり、あなたがそれを知っていますあり、それを要求delete
しなさい。
私はフレームワークでvoid *(別名:不明な型)をコードリフレクションやその他のあいまいさの妙技で使用しましたが、これまでのところ、どのコンパイラからも問題(メモリリーク、アクセス違反など)は発生していません。非標準の操作による警告のみ。
不明(void *)を削除することは完全に理にかなっています。ポインタがこれらのガイドラインに従っていることを確認してください。そうしないと、意味がなくなる場合があります。
1)不明なポインターは、簡単なデコンストラクターを持つ型を指してはなりません。そのため、不明なポインターとしてキャストされた場合、決して削除してはなりません。不明なポインタは、元の型にキャストした後でのみ削除してください。
2)インスタンスはスタックバウンドまたはヒープバウンドメモリで不明なポインタとして参照されていますか?不明なポインタがスタック上のインスタンスを参照している場合は、削除しないでください。
3)不明なポインタが有効なメモリ領域であると100%肯定していますか?いいえ、それは決して削除されるべきではありません!
全体として、不明な(void *)ポインター型を使用して実行できる直接的な作業はほとんどありません。ただし、間接的に、void *は、C ++開発者がデータのあいまいさが必要な場合に信頼できる優れた資産です。
バッファだけが必要な場合は、malloc / freeを使用してください。new / deleteを使用する必要がある場合は、簡単なラッパークラスを検討してください。
template<int size_ > struct size_buffer {
char data_[ size_];
operator void*() { return (void*)&data_; }
};
typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer
OpaqueBuffer* ptr = new OpaqueBuffer();
delete ptr;
charの特定の場合。
charは、特別なデストラクタを持たない組み込み型です。したがって、リークの引数は根拠のないものです。
sizeof(char)は通常1であるため、alignment引数もありません。sizeof(char)が1でないまれなプラットフォームの場合、charに対して十分に整列されたメモリを割り当てます。したがって、整列の議論も根拠のないものです。
この場合、malloc / freeの方が高速です。しかし、あなたはstd :: bad_allocを放棄し、mallocの結果をチェックする必要があります。グローバルなnewおよびdeleteオペレーターを呼び出すと、仲介者を回避できるため、より適切な場合があります。
new
実際にはスローするように定義されていると人々は考えていません。本当じゃない。コンパイラとコンパイラスイッチに依存します。たとえば、MSVC2019 /GX[-] enable C++ EH (same as /EHsc)
スイッチを参照してください。また、組み込みシステムでは、多くの人がC ++例外のパフォーマンス税を支払わないことを選択しています。したがって、「しかし、あなたはstd :: bad_alloc ...を失う」という文は疑わしいものです。