.dll
C++ でクラス ライブラリを作成する場合、動的 ( 、 ) ライブラリ.so
と静的 ( .lib
、 ) ライブラリのどちらかを選択できます.a
。それらの違いは何ですか?また、いつどちらを使用するのが適切ですか?
18 に答える
静的ライブラリは、バイナリ内のコードのサイズを増やします。それらは常にロードされ、コンパイルしたコードのバージョンが実行されるコードのバージョンになります。
動的ライブラリは個別に保存され、バージョン管理されます。更新が元のバージョンとバイナリ互換性があると見なされる場合、コードに同梱された元のバージョンではないダイナミック ライブラリのバージョンが読み込まれる可能性があります。
さらに、動的ライブラリは必ずしも読み込まれるとは限りません。通常、最初に呼び出されたときに読み込まれます。また、同じライブラリを使用するコンポーネント間で共有できます (複数のデータの読み込み、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:
type_info
1 つのスタティック ライブラリを複数の DLL にリンクすると、同じクラスに対して複数のオブジェクトが作成される可能性があります。あなたのプログラムがそれtype_info
が「シングルトン」データであると仮定して&typeid()
orを使用するとtype_info::before()
、望ましくない驚くべき結果が得られる可能性があります。
lib は、アプリケーションの実行可能ファイルにバンドルされているコードの単位です。
dll は、実行可能コードのスタンドアロン ユニットです。そのコードへの呼び出しが行われたときにのみ、プロセスにロードされます。dll を複数のアプリケーションで使用し、複数のプロセスで読み込むことができますが、ハード ドライブにはコードのコピーが 1 つしかありません。
DLL の長所: 複数の製品間でコードを再利用/共有するために使用できます。必要に応じてプロセス メモリにロードし、不要なときにアンロードできます。プログラムの残りの部分から独立してアップグレードできます。
DLL の短所: DLL の読み込みとコードのリベースによるパフォーマンスへの影響。バージョン管理の問題 (「dll 地獄」)
ライブラリの長所: コードは常にプロセスにロードされ、リベースされないため、パフォーマンスへの影響はありません。バージョン管理の問題はありません。
Lib cons : executable/process "bloat" - すべてのコードは実行可能ファイルにあり、プロセスの開始時にロードされます。再利用/共有なし - 各製品には独自のコードのコピーがあります。
静的ライブラリの作成
$$:~/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]>
静的ライブラリがクライアントにコンパイルされます。.lib はコンパイル時に使用され、ライブラリの内容は実行可能ファイルの一部になります。
動的ライブラリは実行時にロードされ、クライアントの実行可能ファイルにはコンパイルされません。動的ライブラリは、複数のクライアント実行可能ファイルが DLL をロードしてその機能を利用できるため、より柔軟です。これにより、クライアント コードの全体的なサイズと保守性も最小限に抑えられます。
時間の経過に伴う変更、バージョン管理、安定性、互換性などについて慎重に検討する必要があります。
共有コードを使用する 2 つのアプリがある場合、相互に互換性を持たせる必要がある場合に備えて、それらのアプリを一緒に強制的に変更しますか? 次に、dll を使用します。すべてのexeは同じコードを使用します。
それとも、一方を変更して他方を壊していないことを確信できるように、それらを互いに分離したいですか。次に、静的ライブラリを使用します。
DLL地獄は、おそらく静的ライブラリを使用する必要があったのに、代わりにdllを使用したため、すべてのexeがそれと互換性があるわけではない場合です。
静的ライブラリは、最終的な実行可能ファイルにリンクする必要があります。それは実行可能ファイルの一部になり、どこへ行ってもそれに従います。動的ライブラリは、実行可能ファイルが実行されるたびにロードされ、DLL ファイルとして実行可能ファイルから分離されたままになります。
実行可能ファイルを再リンクせずにライブラリが提供する機能を変更できるようにする場合は、DLL を使用します (実行可能ファイルを置き換えることなく、DLL ファイルを置き換えるだけです)。
動的ライブラリを使用する理由がなければ、静的ライブラリを使用します。
このトピックに関する優れた議論については、Sunのこの記事をお読みください。
介在するライブラリを挿入できるなど、すべての利点が含まれます。介在の詳細については、こちらの記事を参照してください。
本当にあなたが(大規模なプロジェクトで)行っているトレードオフは、初期ロード時間です.ライブラリは時々リンクされます.決定する必要があるのは、コンパイラが必要とするのに十分な時間がかかるリンクです.弾丸を噛んで前もってそれを行うか、動的リンカーがロード時にそれを行うことができます。
ライブラリを複数の実行可能ファイルで共有する場合は、実行可能ファイルのサイズを小さくするためにライブラリを動的にすることが理にかなっていることがよくあります。それ以外の場合は、必ず静的にします。
dll の使用にはいくつかの欠点があります。それをロードおよびアンロードするための追加のオーバーヘッドがあります。追加の依存関係もあります。dll を変更して executalbes との互換性をなくすと、それらは動作しなくなります。一方、静的ライブラリを変更しても、古いバージョンを使用してコンパイルされた実行可能ファイルは影響を受けません。
私たちのプロジェクトでは、多くの DLL (> 100) を使用しています。これらの DLL には相互に依存関係があるため、動的リンクのセットアップを選択しました。ただし、次の欠点があります。
- 起動が遅い (> 10 秒)
- Windows は名前の一意性に基づいてモジュールをロードするため、DLL はバージョン管理する必要がありました。そうしないと、独自に作成されたコンポーネントが間違ったバージョンの DLL を取得します (つまり、独自の分散セットではなく、既にロードされているもの)。
- オプティマイザーは、DLL 境界内でのみ最適化できます。たとえば、オプティマイザーは頻繁に使用されるデータとコードを隣り合わせに配置しようとしますが、これは DLL の境界を越えて機能しません。
たぶん、より良いセットアップは、すべてを静的ライブラリにすることでした (したがって、実行可能ファイルは 1 つだけです)。これは、コードの重複が発生しない場合にのみ機能します。テストではこの仮定が支持されているようですが、公式の MSDN の引用は見つかりませんでした。たとえば、次のように 1 つの exe を作成します。
- exeはshared_lib1、shared_lib2を使用
- shared_lib1 は shared_lib2 を使用します
- 共有_lib2
shared_lib2 のコードと変数は、最終的にマージされた実行可能ファイルに一度だけ存在する必要があります。誰でもこの質問をサポートできますか?
ライブラリが静的である場合、リンク時にコードが実行可能ファイルにリンクされます。これにより、実行可能ファイルが大きくなります (動的ルートを使用した場合よりも大きくなります)。
ライブラリが動的である場合、リンク時に必要なメソッドへの参照が実行可能ファイルに組み込まれます。これは、実行可能ファイルと動的ライブラリを出荷する必要があることを意味します。また、ライブラリ内のコードへの共有アクセスが安全であるかどうか、とりわけ読み込みアドレスが優先されるかどうかを検討する必要があります。
静的ライブラリを使用できる場合は、静的ライブラリを使用してください。
大規模なコードベースがあり、すべてが下位レベルのライブラリ (Utils や Gui フレームワークなど) の上に構築されている場合、より管理しやすいライブラリに分割して、それらを静的ライブラリにするという一般的な経験則があります。動的ライブラリは実際には何も購入せず、驚きも少なくなります。たとえば、シングルトンのインスタンスは 1 つだけです。
コードベースの残りの部分とは完全に分離されているライブラリ (サード パーティのライブラリなど) がある場合は、それを dll にすることを検討してください。ライブラリが LGPL の場合、ライセンス条件により、とにかく dll を使用する必要がある場合があります。
組み込みプロジェクトまたは特殊なプラットフォームの静的ライブラリで作業することが唯一の方法である場合、多くの場合、それらをアプリケーションにコンパイルする手間がかかりません。また、すべてを含むプロジェクトとメイクファイルがあると、人生がより楽しくなります。
静的ライブラリは、コードが実行可能ファイルにコンパイルされるアプリケーションにリンクされると、ライブラリのオブジェクト コードを含むアーカイブです。共有ライブラリは、実行可能ファイルにコンパイルされないという点で異なります。代わりに、動的リンカは必要なライブラリを探していくつかのディレクトリを検索し、それをメモリにロードします。複数の実行可能ファイルが同じ共有ライブラリを同時に使用できるため、メモリ使用量と実行可能ファイルのサイズが削減されます。ただし、実行可能ファイルと共に配布するファイルは他にもあります。リンカーがライブラリを見つけることができる場所にライブラリが uses システムにインストールされていることを確認する必要があります。静的リンクはこの問題を解消しますが、実行可能ファイルが大きくなります。