静的変数の使用と実行可能モジュールの静的リンク

こんばんは!



今日は、実行可能モジュールのリンクが正しくない静的変数の陰湿な機能について説明します。 誰もが経験できる実際の練習からの問題を示します。

私はすべてをかなり詳細に噛んでいるので、「経験のある」人や目の肥えた人は、私が「サンドボックスをつついている」という感覚を感じるかもしれませんが、この記事は彼らだけのものではありません。



状況を想像してみましょう:静的ライブラリ(lib)に実装されたクラスがあります。 このライブラリは、実装モジュール(dll)によって静的にバインドされています。 さらに、この実行可能モジュール(exe)もこのdllを静的にバインドします。 さらに、Exeモジュールは静的ライブラリ(lib)を静的にリンクします。

このようなもの:



画像



たとえば、次のロジックがあります。libには、何らかのツールが実装されています。 dllには、このツールに基づいたいくつかの機能があります。 エグゼは、この機能のテストを実装しています。 DLL自体は(lib'eにある)ツールクラスをエクスポートしないため、テストではlibの静的リンクが必要です。

インストルメンタルクラスに静的変数を含めます。 また、dllにはこのクラスを作成する関数があり、オブジェクトは値によって返されます。

拡張された概要は次のとおりです。



画像

C ++コードは次のとおりです。



コードからわかるように、コピーコンストラクターが呼び出されるようにRVOをバイパスするのに役立つ特別な関数fooがあります。 dllモジュールとexeモジュールの両方が互いに独立してアセンブルされるため、lib内の静的変数の存在を認識して、独自に作成する必要があることを思い出させてください。



ListAndIterクラスのオブジェクトはコピーコンストラクターを介して返されるため、exeモジュール側でオブジェクトを受け取ると、静的変数へのすべての参照が無効になります。 手順では、次のようになります。

  1. * .exe:GetStaticObj()関数を呼び出します。
  2. Dll.dll:ListAndIterクラスの一時オブジェクトの作成。 ゼロがリストに入れられ、反復子イテレーターがそれを指します。 さらに、この時点で、exeモジュール側の静的変数はそれぞれ空であり、反復子は無効です。
  3. Dll.dll:クラスListAndIterのオブジェクトのコピーコンストラクターが呼び出されます。 イテレータが一時オブジェクトに対して無効になりました。 新しいオブジェクトの場合、イテレータはDLL.dllのリストを指しますが、オブジェクト自体はexeモジュールの側で作成されます。
  4. Dll.dll:ListAndIterクラスの一時オブジェクトを破壊しました。 イテレータは無効であるため、アクションは発生しません。
  5. * .exe:objオブジェクトのデストラクタが呼び出されます。 イテレータをgetList()。End()と比較しようとすると、Windowsエラーがポップアップします:「イテレータは互換性がありません。」 つまり、「その他のリスト」のイテレーターです。


静的ライブラリへのexeモジュールの依存関係を削除することにより、この状況を修正してみましょう。 次に、静的ライブラリのすべての機能をdll経由でエクスポートする必要があります(以下のコードを参照)。



画像

コードの変更:

これで、オブジェクトはdll側でのみ作成および削除されます。 exeモジュールには静的変数はなく、そのようなコードは正常に機能します。



ListAndIterクラスがボイラープレートになった場合にどうなるかを考えてみましょう。



画像



テンプレートとそのようなクラスのすべてのオブジェクトの完全な特殊化ごとに、静的変数が必要です。

まず、ヘッダーファイルにテンプレートクラスの実装を配置する必要があります。 テンプレートはコンパイル段階で公開されます。

静的変数がクラスのメンバーである場合、プロジェクトを正常にアセンブルするために、使用されるすべてのモジュールでこれらの変数を明示的に初期化する必要があります。 この場合、2つの静的変数を明示的に作成し、最初の例に戻ります。

それ以外の場合、静的変数がクラスのメンバーではなく、静的メソッドを介して作成される場合、この場合も静的変数は作成されますが、すでに暗黙的に作成されています。 エラーが再び繰り返されます。



この状況を解決するには、この機能を配置するための中間ラ​​イブラリを作成する必要があります。 つまり、dllの代わりにlibを実行します。 その後、再び1つの静的変数が残ります。



結論 :静的ライブラリで静的変数を使用する場合、実行可能モジュールが互いに静的にリンクしないようにする必要があります。



依存関係を単純化しても問題が解決しない場合があります。 たとえば、クラスは静的ライブラリに実装され、特定の静的インスタンスカウンターがあります。 この静的ライブラリは2つの異なるdllにリンクするため、2つの異なるカウンタが作成されます。 この場合、静的ライブラリを動的ライブラリ(dll)に変えることで問題は解決します。 したがって、他の2つのdllは新しいdllを動的にリンクします。 その場合、静的変数は1つのdllにのみ存在します(カウンターを持つクラスが実装されているdllに存在します)。



すべてのコードはgithubから取得できます。



PS私は多くのことを書きましたが、完璧ではないかもしれません...私はアドバイスやコメントを喜んでいます。



All Articles