1

5.5k 行の C++ DLL のリファクタリングに取り組んでおり、それを多数の小さな DLL に分割しています。残念なことに、多くのコードが結合されており、GiantDLL を分割する過程で、いくつかの循環参照を導入しました。

すなわち、

//In emergeOSLib.h:
DLL_EXPORT std::wstring ELGetProcessIDApp(DWORD processID, bool fullName);

//In a couple of functions in emergeOSLib.cpp:
ELMessageBox(GetDesktopWindow(), messageText, (WCHAR*)TEXT("Emerge Desktop"),                       ELMB_OK|ELMB_ICONERROR|ELMB_MODAL);

//In emergeUtilityLib.h:
DLL_EXPORT int ELMessageBox(HWND hwnd, std::wstring messageText, std::wstring messageTitle, DWORD msgFlags);

//In a function in emergeUtilityLib.cpp:
out << ELGetProcessIDApp(GetCurrentProcessId(), false) << TEXT(": ") << debugText << std::endl;

そして出来上がり、1 つの循環参照。他にもあると思いますが、これは私が今扱っているものだけです。

私はいくつかの調査を行ったところ、前方宣言が進むべき道のように思われることがわかりました:
解決ヘッダーには循環依存関係が含まれています
C++ の循環依存関係

2 番目のリンクは、前方宣言がs よりも望ましいことを示唆してい#includeます。

この時点で 2 つの質問があります。まず、別の DLL にある関数を前方宣言するにはどうすればよいでしょうか。私の理解では、コンパイラはまだ前方宣言された関数を見つけることができず (最初の DLL の一部として 2 番目の DLL をコンパイルしていないため)、文句を言うでしょう。第二に、一般的にどちらがより良い慣行と考えられていますか、#include声明または前方宣言ですか?

4

2 に答える 2

1

前方宣言#includeと宣言の循環性のみを支援します。しかし、定義の循環性があります。すべての定義を一緒にリンクしている場合、マルチパス リンカーはこれを解決しますが、そうではありません。

これを行うには、基本的に 2 つの方法があります。まず、ELGetProcessIDAppユーティリティ ライブラリ内の関数ポインターに変更できます。最初はユーティリティ ライブラリ内のスタブ値を指し、OS サポート ライブラリが読み込まれると、その関数ポインタが OS 固有の実装で上書きされます。

または、リンカー定義ファイルを使用して、DLL が実際に存在する前にインポート ライブラリを構築することもできます。これにより、一連の循環依存 DLL が作成され、リンカーの問題が修正されます。動作する可能性はありますが、驚くべきロード順序の影響を引き起こし、グローバルな初期化順序の大失敗を示す可能性もあります。

または、単一の DLL を使用して、リファクタリングをソース ファイルの分割に制限します。

于 2013-10-31T06:04:56.073 に答える
0

別の DLL にある関数を前方宣言するにはどうすればよいですか?

あなたはいけません、それはうまくいきません。宣言することはできますが、リンク ステージは失敗します。

代わりに、DLL A が DLL B に対して通常どおりリンクし、DLL B が実行時に DLL A から関数ポインタ (または仮想関数を備えたオブジェクト) を受け取るようにします。

于 2013-10-31T06:04:37.087 に答える