C++でクラスライブラリを作成するときは、動的(.dll
、.so
)ライブラリと静的(.lib
、.a
)ライブラリのいずれかを選択できます。両者の違いは何ですか?またどちらを使用するのが適切か?
静的ライブラリはバイナリのコードサイズを大きくします。それらは常にロードされており、あなたがコンパイルしたコードのどんなバージョンでも実行されるコードのバージョンです。
動的ライブラリーは別々に保管およびバージョン管理されます。あなたのコードと一緒に出荷されたオリジナルのものではなかった動的ライブラリのバージョンがロードされることは可能ですifアップデートはオリジナルのバージョンとバイナリ互換性があると見なされます。
さらに動的ライブラリは必ずしもロードされる必要はなく、通常は最初に呼び出されたときにロードされ、同じライブラリを使用するコンポーネント間で共有できます(複数のデータロード、1つのコードロード)。
動的ライブラリは、ほとんどの場合より優れたアプローチであると考えられていましたが、もともと大きな欠陥(google DLL地獄)があり、最近のWindows OS(Windows XP特に)。
静的ライブラリとは何かを適切に説明している人もいますが、少なくともWindowsでは、静的ライブラリを使用する際の注意点をいくつか指摘したいと思います。
シングルトン:グローバル/スタティックでユニークなものが必要な場合は、それをスタティックライブラリに入れることに非常に注意してください。複数のDLLがその静的ライブラリに対してリンクされている場合、それらはそれぞれ独自のシングルトンのコピーを取得します。ただし、アプリケーションがカスタムDLLを持たない単一のEXEである場合、これは問題にならない可能性があります。
参照されていないコードの削除:静的ライブラリに対してリンクすると、DLL/EXEによって参照されている静的ライブラリの部分だけがリンクされます。 DLL/EXE。
たとえば、mylib.lib
にa.obj
およびb.obj
が含まれていて、DLL/EXEがa.obj
の関数または変数のみを参照している場合、b.obj
の全体がリンカによって破棄されます。 b.obj
がグローバル/静的オブジェクトを含む場合、それらのコンストラクタとデストラクタは実行されません。それらのコンストラクタ/デストラクタに副作用がある場合、あなたはそれらがないことに失望するかもしれません。
同様に、静的ライブラリに特別なエントリポイントが含まれている場合は、それらが実際に含まれるように注意する必要があります。組み込みプログラミング(Windowsではなく大丈夫です)におけるこの例は、特定のアドレスにあるものとしてマークされている割り込みハンドラです。また、割り込みハンドラをエントリポイントとしてマークして、それが破棄されないようにする必要があります。
これによるもう1つの結果は、未解決の参照のために静的ライブラリに完全に使用できないオブジェクトファイルが含まれる可能性がありますが、それらのオブジェクトファイルから関数または変数を参照するまでリンカエラーは発生しません。これはライブラリが書かれてからずっと後に起こるかもしれません。
デバッグシンボル:静的ライブラリごとに別々のPDBが必要な場合もありますし、デバッグシンボルをオブジェクトファイルに配置してデバッグすることもできます。 DLL/EXEのPDBにロールイン。 Visual C++のドキュメントでは 必要なオプション について説明しています。
RTTI:単一の静的ライブラリを複数のDLLにリンクすると、同じクラスに対して複数のtype_info
オブジェクトが作成されることがあります。あなたのプログラムがtype_info
が "シングルトン"データであると仮定し、そして&typeid()
またはtype_info::before()
を使うならば、あなたは望ましくないそして驚くべき結果を得るかもしれません。
Libは、アプリケーションの実行可能ファイルにバンドルされているコードの単位です。
Dllは、実行可能コードのスタンドアロン単位です。そのコードに呼び出しが行われたときにのみ、プロセスにロードされます。ハードドライブにコードのコピーが1つしかない状態で、dllを複数のアプリケーションで使用し、複数のプロセスに読み込むことができます。
Dll pros:複数の製品間でコードを再利用/共有するために使用できます。必要に応じてプロセスメモリにロードし、不要なときはアンロードできます。プログラムの他の部分とは無関係にアップグレードすることができます。
Dll cons:dllのロードとコードのリベースのパフォーマンスへの影響。バージョン管理の問題( "dll hell")
Lib pros:コードは常にプロセスに読み込まれ、リベースされないため、パフォーマンスへの影響はありません。バージョン管理の問題はありません。
Lib cons:executable/process "bloat" - すべてのコードはあなたの実行ファイルにあり、プロセス起動時にロードされます。再利用/共有なし - 各製品には独自のコードのコピーがあります。
静的ライブラリーと動的ライブラリーの技術的な関係(静的ファイルは、いくつかの異なる実行可能ファイル間でコードを共有できるようにする1つの大きなバイナリと動的ライブラリーをまとめたもの)の他に、法的意味があります。
たとえば、LGPLライセンスのコードを使用していて、LGPLライブラリに対して静的にリンクする(したがって1つの大きなバイナリを作成する)場合、コードは自動的にOpen Sourced( 自由のように無料) LGPLコードになります。共有オブジェクトにリンクしている場合は、LGPLライブラリ自体に加えた改善点/バグ修正のみをLGPLに必要とします。
たとえば、モバイルアプリケーションのコンパイル方法を決定している場合、これははるかに重要な問題になります(Androidでは静的と動的の選択があり、iOSではそうではありません - 常に静的です)。
$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H
void foo();
#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H
void foo2();
#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
cc -o hello hello.o -L. -ltest
hello.o: hello.c
cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
ar cr libtest.a foo.o foo2.o
foo.o:foo.c
cc -c foo.c
foo2.o:foo.c
cc -c foo2.c
clean:
rm -f foo.o foo2.o libtest.a hello.o
$$:~/static [38]>
$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H
void foo();
#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H
void foo2();
#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
cc -o hello hello.o -L`pwd` -ltest
hello.o:
cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
cc -c -b foo.c
foo2.o:foo.c
cc -c -b foo2.c
clean:
rm -f libtest.sl foo.o foo
2.o hello.o
$$:~/dynamic [50]>
C++プログラムは2段階で構築されています
静的ライブラリ(.lib)は単なる.objファイルの集まりであり、したがって完全なプログラムではありません。それはプログラムを構築する第二(リンク)段階を経ていません。一方、DLLは、exeファイルのようなものであり、したがって完全なプログラムです。
あなたが静的ライブラリを構築する場合、それはまだリンクされていないので、あなたの静的ライブラリの消費者はあなたが使ったのと同じコンパイラを使わなければならないでしょう(あなたがg ++を使ったなら、彼らはg ++を使わなければならないでしょう)。
代わりにあなたがdllを構築した(そしてそれを構築した場合 正しく )、あなたは彼らが使用しているコンパイラに関係なく、すべての消費者が使用できる完全なプログラムを構築した。ただし、クロスコンパイラ互換性が必要な場合は、dllからのエクスポートに関していくつかの制限があります。
時間の経過による変化、バージョン管理、安定性、互換性などについて慎重に検討する必要があります。
共有コードを使用する2つのアプリケーションがある場合、それらが互いに互換性がある必要がある場合に備えて、それらのアプリケーションを強制的に一緒に変更しますか?その後、DLLを使用してください。すべてのexeは同じコードを使用します。
または、互いを切り離して、一方を変更し、もう一方を壊していないと確信できるようにしますか。それからstatic libを使います。
DLL地獄はおそらくあなたが静的ライブラリを使用すべきであるべきであるときです、しかしあなたは代わりにdllを使用しました、そしてすべてのexesはそれと互換性がありません。
静的ライブラリはクライアントにコンパイルされます。 .libはコンパイル時に使用され、ライブラリの内容は消費する実行ファイルの一部になります。
動的ライブラリは実行時にロードされ、クライアントの実行可能ファイルにはコンパイルされません。複数のクライアント実行可能ファイルがDLLをロードしてその機能を利用できるため、動的ライブラリはより柔軟です。これにより、クライアントコードの全体的なサイズと保守性も最小限に抑えられます。
静的ライブラリは最終的な実行可能ファイルにリンクされなければなりません。それは実行ファイルの一部になり、どこへ行ってもそれをたどります。実行可能ファイルが実行されるたびに動的ライブラリがロードされ、DLLファイルとして実行可能ファイルとは別のままになります。
実行可能ファイルを再リンクせずにライブラリによって提供される機能を変更できるようにする場合は、DLLを使用します(DLLファイルを置き換える必要はありません)実行可能ファイル).
動的ライブラリを使用する理由がない場合はいつでも静的ライブラリを使用します。
" 共有ライブラリの書き方 "に関するUlrich Drepperの論文もまた、共有ライブラリを最大限に活用する方法、または彼が "動的共有オブジェクト"(DSO)と呼んでいるものを詳述した優れたリソースです。これは、 ELF バイナリ形式の共有ライブラリに重点を置いていますが、Windows DLLにも適した議論がいくつかあります。
実際に(大規模プロジェクトで)行っているトレードオフは初期ロード時です。ライブラリーはいつかリンクされます。リンクはコンパイラーが必要とするのに十分な時間がかかるようにする必要があります。弾丸を噛んで前もってそれをするか、あるいは動的リンカがロード時にそれをすることができます。
ライブラリを複数の実行可能ファイルで共有する場合は、実行可能ファイルのサイズを減らすために動的にすることが理にかなっています。それ以外の場合は、確実に静的にします。
Dllを使用することのいくつかの不利な点があります。ロードとアンロードには追加のオーバーヘッドがあります。追加の依存関係もあります。あなたがそれをあなたのexecutalbesと両立しないようにするためにあなたがdllを変えるならば、彼らは働かなくなります。一方、静的ライブラリを変更しても、古いバージョンを使用してコンパイルされた実行可能ファイルは影響を受けません。
ライブラリが静的な場合、リンク時にコードは実行可能ファイルとリンクされます。これにより、実行可能ファイルが大きくなります(動的ルートを行った場合よりも)。
ライブラリが動的な場合は、リンク時に必要なメソッドへの参照が実行可能ファイルに組み込まれます。つまり、実行可能ファイルと動的ライブラリを出荷する必要があります。また、ライブラリ内のコードへの共有アクセスが安全であるかどうか、他のものの中で優先ロードアドレスを検討する必要があります。
あなたが静的ライブラリと一緒に暮らすことができるならば、静的ライブラリと一緒に行ってください。
静的ライブラリは、そのコードが実行可能ファイルにコンパイルされるアプリケーションにリンクされているときに、ライブラリのオブジェクトコードを含むアーカイブです。共有ライブラリは、実行可能ファイルにコンパイルされていないという点で異なります。代わりに、動的リンカはいくつかのディレクトリを検索して必要なライブラリを探し、それをメモリにロードします。複数の実行可能ファイルが同じ共有ライブラリを同時に使用できるため、メモリ使用量と実行可能ファイルのサイズが削減されます。ただし、実行可能ファイルと一緒に配布するファイルは他にもあります。あなたはライブラリがリンカがそれを見つけることができるどこかの用途システムにインストールされていることを確認する必要があります。
私たちのプロジェクトではたくさんのDLL(> 100)を使っています。これらのDLLは互いに依存関係にあるので、動的リンクの設定を選択しました。ただし、次のような欠点があります。
たぶんもっと良い設定は、すべて静的ライブラリにすることでした(したがって、実行ファイルは1つだけです)。これはコードの重複が発生しない場合にのみ機能します。テストはこの前提をサポートしているようですが、私は公式のMSDN見積もりを見つけることができませんでした。だから、例えば1 exeを作る:
Shared_lib2のコードと変数は、最後にマージされた実行可能ファイルに一度だけ存在していなければなりません。誰もがこの質問を支持することができますか?
もしあなたが組み込みプロジェクトや特殊なプラットフォームの静的ライブラリで作業することが唯一の方法であるならば、多くの場合それらはあなたのアプリケーションにコンパイルするのが面倒ではありません。また、すべてを含むプロジェクトとメークファイルがあると、人生が幸せになります。
一般的な経験則として、大規模なコードベースがすべて低レベルのライブラリ(UtilsやGuiフレームワークなど)の上に構築されている場合は、それらを管理しやすいライブラリに分割してから静的ライブラリにします。動的ライブラリは実際には何も購入しませんし、驚くべきこともありません。たとえば、シングルトンのインスタンスは1つだけです。
コードベースの他の部分とはまったく別のライブラリ(例えばサードパーティのライブラリ)を持っているなら、それをdllにすることを検討してください。ライブラリがLGPLであるなら、あなたはとにかくライセンス条件のためにdllを使う必要があるかもしれません。