質問
ライブラリで new
キーワード(メインアプリとは異なるビルド)を使用する場合、メインアプリで delete
を使用して削除すると、クラッシュ/エラーが発生する可能性はありますか?
解決
はい、そうです。特に、デバッグ/リリースヒープが異なるという問題があります。また、ライブラリが新しい配置を使用している場合や、問題が発生するカスタムヒープを使用している場合もあります。ただし、デバッグ/リリースの問題は最も一般的です。
他のヒント
それは依存します。静的ライブラリについて話している場合は、おそらく大丈夫です。コードは、同じC ++ランタイムライブラリを使用して、メインプログラムと同じコンテキストで実行されます。つまり、 new
と delete
は同じヒープを使用します。
共有ライブラリ(DLL)について話しているなら、おそらく大丈夫ではないでしょう。 DLLで実行されているコードは、異なるC ++ランタイムライブラリを使用している可能性があります。つまり、ヒープのレイアウトが異なります。 DLLはまったく異なるヒープを使用している可能性があります。
DLLによって割り当てられたポインター(またはその逆)で(メインプログラムで) delete
を呼び出すと、(せいぜい)すぐにクラッシュするか、(ひどく)メモリ破損が発生します。しばらく時間がかかります。
いくつかのオプションがあります。 1つ目は、「ファクトリーメソッド」を使用することです。これらのオブジェクトを作成および削除するパターン:
Foo *CreateFoo();
void DeleteFoo(Foo *p);
これらはヘッダーファイルに実装しないでください 。
別の方法として、オブジェクトで Destroy
メソッドを定義できます:
class Foo
{
~Foo();
public:
virtual void Destroy();
};
...もう一度、ヘッダーファイルにこれを実装しないでください。このように実装します:
void Foo::Destroy()
{
delete this;
// don't do anything that accesses this object past this point.
}
Fooのデストラクタはプライベートであるため、 Foo :: Destroy
を呼び出す必要があります。
Microsoft COMは、参照カウントがゼロになったときにオブジェクトを削除する Release
メソッドを定義する同様のことを行います。
はい、できます。簡単な解決策は、メインアプリケーションから呼び出すことができる作成および削除機能をライブラリに提供することです。 Create関数は新しいものを実行し、ポインターを返します。ポインターは後で削除のためにDelete関数に渡されます。
これはWindowsでしか見られない問題です。
Unixishシステムは、共有ライブラリを同じプログラム内の同じライブラリの異なるバージョンに強制的にリンクさせる習慣を持たず、ロードされたすべてのシンボルはグローバルに表示されます。つまり、オブジェクトがコードの一部で割り当てられ、別の部分で削除された場合、両方が同じシステムライブラリを使用してそれを実行します。
私が言わなければならないのは、WindowsがさまざまなCランタイムDLLで作成するこの問題は、Cプログラマーにとって本当に迷惑で不自然なことです。 Cライブラリを見てください。文字列をmallocし、プログラマがその上でfree()を呼び出すことを期待するstrdupのような関数があります。ただし、Windows上の独自のライブラリで同じことを行い、爆発を待ちます。開発中は発生しませんが、コンパイルされたDLLを他の貧しい樹液に渡した後にのみ発生するため、待機する必要があります。
Old New Thing がこれについて説明しました。 。また、Microsoftの主なソリューションのリストも提供しています。
そこに問題があることはかなり正しいですが、ほとんどの場合、他の回答(これまで)が提案したものよりもさらに簡単な解決策があります。 newの使用を継続し、自由に削除できます。必要なのは、DLLの境界を越えて使用される可能性のあるライブラリ内のクラスごとにnewをオーバーロードして削除することだけです。
個人的に、必要な機能を提供するために単純なクラスを定義しました:
class NewDelete
{
public:
void *operator new (size_t size);
void operator delete (void *memory);
void *operator new (size_t size, void *ptr);
void operator delete (void *memory, void *ptr);
};
これら4つのメンバー関数がすべて同じDLLで定義されている限り、このクラスから派生するクラスは自動的に「DLLセーフ」になります。 -DLLの境界を気にすることなく、newおよびdeleteを通常どおり使用できます。