DLLを動的にロードするexeを使用しています。DLL内の関数は、ヒープにメモリを割り当て、そのメモリへのポインタをexeに渡します。
先輩はそうするのは悪い習慣だと言います。彼は、exeとDLLの間でメモリを共有する必要がある場合、exeはメモリを割り当て、そのポインタをDLLに渡す必要があり、その逆はないと言います。これは本当ですか?なんで?
編集:私の場合、DLL自体の内部でメモリの割り当てと割り当て解除を計画しました。
DLLを動的にロードするexeを使用しています。DLL内の関数は、ヒープにメモリを割り当て、そのメモリへのポインタをexeに渡します。
先輩はそうするのは悪い習慣だと言います。彼は、exeとDLLの間でメモリを共有する必要がある場合、exeはメモリを割り当て、そのポインタをDLLに渡す必要があり、その逆はないと言います。これは本当ですか?なんで?
編集:私の場合、DLL自体の内部でメモリの割り当てと割り当て解除を計画しました。
呼び出し元にポインターを提供させるいくつかの理由を次に示します。
malloc
/の別のバージョンに対してリンクされている可能性があります。(たとえば、DLL はリリース バージョンを使用し、 は特殊なデバッグ バージョンを使用している可能性があります)。free
.exe
malloc
free
.exe
malloc
なく、特定のメモリ プールからメモリを割り当てたいとします。おそらく、呼び出し元がスタックに割り当てられたメモリへのポインターを提供できる場合です。DLL 自体がメモリを割り当てた場合、呼び出し元にはこれらのオプションはありません。.exe
(2 番目と 3 番目のポイントも、DLL コードが使用するアロケーター/デアロケーターを提供することでほとんど解決できます。)
デザインパターンの背後にある基本的な考え方の1つは、所有権です。アイデアは-one who creates a resource (and thereby holds it in the pointer) should be responsible for deleting the resource
です。これにより、設計の神聖さが保証され、プロジェクトの寿命が長くなると、開発者はバグを少なくすることができます。
したがって、あなたの場合、DLLは任意の実行可能ファイルでアタッチでき、リソースの削除を試みることができます。これにより、将来問題が発生する可能性があります。ですから、その逆もあると思いますし、それは確かなアドバイスだと思います。
この問題は以前にも見たことがありますが、これは DLL と exe の CRT へのリンクが異なる (静的、動的 MT など) ことが原因です。
DLL と実行可能ファイルの間でメモリへのポインタを渡すつもりですが、どちらもFree()
それぞれのヒープからメモリを解放する何らかの機能を提供する必要があります。
一般に、ヒープ (The One Heap) はプロセスに属し、どこから割り当てても問題ないため、そうでない場合を除いて、これは問題なく機能します。
したがって、それが「悪い習慣」であるという主張は有効です。正常に機能しない場合を除いて、正常に機能するものよりも悪いことはほとんどありません。
これの最悪の部分は、すべてが爆発したときに、何が問題なのかがすぐにはわからず、知らないうちに簡単に問題に遭遇する可能性があることです. 特定のバージョンの CRT を DLL にリンクするのと同じくらい簡単かもしれません。または、チームの誰かが何らかの理由で別のヒープを作成した可能性があります。または、別のヒープが作成される原因となった、すぐには明らかではないその他の理由。
誤ったヒープから解放された状況を非常に悪質なものにしているのは、一般的に何が、いつ起こるか (または誰かが気付くかどうか) がわからないことです。
ヒープ関数または例外から NULL 戻り値を取得する場合があります。あなたのアプリケーションは、それらのいずれかに対して準備されているかもしれませんし、準備されていないかもしれません (正直なところ、あなたは常に戻り値をチェックしていますよね?)。解放するとすぐにクラッシュするか、数分または数時間後に静かにメモリリークしてアドレス空間 (またはメモリ) が不足する可能性があり、その理由は誰にもわかりません。または、他の何か。
したがって、表示される内容は、問題の原因と (明らかに) 相関していない可能性があります。
アロケータを DLL に渡してメモリを割り当てることは完全に安全であり、C++ std ライブラリ自体で使用される標準モデルであることだけを指摘しておきます。この場合、割り当ては呼び出し元から渡されたメソッドを介して DLL によって行われ、ポインターを渡すことを回避し、の異なる実装へのリンクの問題を回避しmalloc()
ます。
exe と dll のヒープは異なる場合があります。どちらかが他方によって割り当てられたメモリを解放しようとすると、解放は失敗し、リークが発生します。
exe と dll の両方が CRT を動的に使用し、同じバージョンの CRT を使用する場合にのみ、同じヒープを使用します。
したがって、同じバイナリで割り当てと解放を行うことは非常に良いアドバイスです。
生のポインタを渡すのは一般的に悪い習慣だと私は主張します。dllの場合、クリーンアップを適切に処理するカスタム削除機能を備えたスマートポインターを返す必要があります(オプションは、、std::unique_ptr
またはstd::shared_ptr
優先boost::shared_ptr
順に)。
実際、使用する方がおそらく安全です。std::weak_ptr
またはboost::weak_ptr
、この間にdllがアンロードされている可能性があるため、リソースにアクセスするたびにチェックする必要があります。
@kumar_m_kiranがすでに指摘したように、所有権の問題についてですが、@alegunaも正しいことに注意したいので、正しいルールは「DLLまたはEXEで同じメモリを割り当てて割り当て解除しますが、両方ではありません」 .
いいえ、それは「悪い習慣」ではありません。私の経験から、適切なメモリ空間でそのポインターを解放するように注意する必要があります。複数の DLL にアセットを割り当てるグラフィック エンジンを作成しました (各 DLL はミニ ゲームを表します)。shared_ptr を使用します (Boost の時点では、C++11 (またはそれ以降) の std::shared_ptr が同じセマンティクスをサポートしていると確信しています)。適切なスペースのメモリを削除する関数を提供します。この時点での主な関心事は、DLL を解放する前に shared_ptrs を確実に解放することです。今は思い出すことはできませんが、DLL/DLL ラッパーが所有する shared_ptr のリストを使用した可能性があり、ポインターの他のすべての使用はその shared_ptr への weak_ptr を介していました。
それは必ずしも悪い習慣ではありませんが、危険な習慣です (そしておそらく悪い習慣です)。誰がメモリを解放する責任があるかを慎重に考える必要があります。exe はおそらく DLL のメモリを直接解放できない (または解放すべきではない) ため、後でこのポインタを DLL に戻すことになるでしょう。そのため、EXE と DLL の間でポインターをやり取りしていますが、これは適切ではありません。