Beim Erstellen einer Klassenbibliothek in C ++ können Sie zwischen dynamischen ( .dll
, .so
) und statischen ( .lib
, .a
) Bibliotheken wählen . Was ist der Unterschied zwischen ihnen und wann ist es angebracht, welche zu verwenden?
Beim Erstellen einer Klassenbibliothek in C ++ können Sie zwischen dynamischen ( .dll
, .so
) und statischen ( .lib
, .a
) Bibliotheken wählen . Was ist der Unterschied zwischen ihnen und wann ist es angebracht, welche zu verwenden?
Antworten:
Statische Bibliotheken vergrößern den Code in Ihrer Binärdatei. Sie werden immer geladen und jede Version des Codes, mit dem Sie kompiliert haben, ist die Version des Codes, der ausgeführt wird.
Dynamische Bibliotheken werden separat gespeichert und versioniert. Es ist möglich, dass eine Version der dynamischen Bibliothek geladen wird, die nicht die Originalversion ist, die mit Ihrem Code geliefert wurde, wenn das Update als binär kompatibel mit der Originalversion angesehen wird.
Außerdem werden dynamische Bibliotheken nicht unbedingt geladen - sie werden normalerweise beim ersten Aufruf geladen - und können von Komponenten gemeinsam genutzt werden, die dieselbe Bibliothek verwenden (mehrere Daten werden geladen, ein Code wird geladen).
Dynamische Bibliotheken wurden die meiste Zeit als der bessere Ansatz angesehen, aber ursprünglich hatten sie einen großen Fehler (Google DLL Hell), der durch neuere Windows-Betriebssysteme (insbesondere Windows XP) so gut wie beseitigt wurde.
Andere haben angemessen erklärt, was eine statische Bibliothek ist, aber ich möchte auf einige der Vorbehalte bei der Verwendung statischer Bibliotheken hinweisen, zumindest unter Windows:
Singletons: Wenn etwas global / statisch und einzigartig sein muss, sollten Sie es sehr vorsichtig in eine statische Bibliothek stellen. Wenn mehrere DLLs mit dieser statischen Bibliothek verknüpft sind, erhalten sie jeweils eine eigene Kopie des Singletons. Wenn Ihre Anwendung jedoch eine einzelne EXE-Datei ohne benutzerdefinierte DLLs ist, ist dies möglicherweise kein Problem.
Entfernen von nicht referenziertem Code: Wenn Sie eine Verknüpfung zu einer statischen Bibliothek herstellen, werden nur die Teile der statischen Bibliothek, auf die Ihre DLL / EXE verweist, mit Ihrer DLL / EXE verknüpft.
Wenn beispielsweise mylib.lib
enthält a.obj
und b.obj
und Ihre DLL / EXE nur Verweise Funktionen oder Variablen aus a.obj
, die Gesamtheit b.obj
wird durch den Linker verworfen bekommen. Wenn sie b.obj
globale / statische Objekte enthalten, werden ihre Konstruktoren und Destruktoren nicht ausgeführt. Wenn diese Konstruktoren / Destruktoren Nebenwirkungen haben, können Sie von ihrer Abwesenheit enttäuscht sein.
Wenn die statische Bibliothek spezielle Einstiegspunkte enthält, müssen Sie möglicherweise darauf achten, dass diese tatsächlich enthalten sind. Ein Beispiel hierfür in der eingebetteten Programmierung (okay, nicht Windows) wäre ein Interrupt-Handler, der als an einer bestimmten Adresse markiert ist. Sie müssen den Interrupt-Handler auch als Einstiegspunkt markieren, um sicherzustellen, dass er nicht verworfen wird.
Eine weitere Folge davon ist, dass eine statische Bibliothek möglicherweise Objektdateien enthält, die aufgrund nicht aufgelöster Referenzen vollständig unbrauchbar sind. Sie verursacht jedoch erst dann einen Linkerfehler, wenn Sie auf eine Funktion oder Variable aus diesen Objektdateien verweisen. Dies kann lange nach dem Schreiben der Bibliothek geschehen.
Debug-Symbole: Möglicherweise möchten Sie für jede statische Bibliothek einen separaten PDB, oder Sie möchten, dass die Debug-Symbole in den Objektdateien platziert werden, damit sie in den PDB für die DLL / EXE übernommen werden. In der Visual C ++ - Dokumentation werden die erforderlichen Optionen erläutert .
RTTI:type_info
Wenn Sie eine einzelne statische Bibliothek mit mehreren DLLs verknüpfen, erhalten Sie möglicherweise mehrere Objekte für dieselbe Klasse. Wenn Ihr Programm davon ausgeht, dass type_info
es sich um "Singleton" -Daten handelt und &typeid()
oder verwendet type_info::before()
, erhalten Sie möglicherweise unerwünschte und überraschende Ergebnisse.
Eine lib ist eine Codeeinheit, die in Ihrer ausführbaren Anwendungsdatei gebündelt ist.
Eine DLL ist eine eigenständige Einheit von ausführbarem Code. Es wird dabei nur geladen, wenn ein Aufruf dieses Codes erfolgt. Eine DLL kann von mehreren Anwendungen verwendet und in mehreren Prozessen geladen werden, wobei immer noch nur eine Kopie des Codes auf der Festplatte vorhanden ist.
DLL-Profis : können verwendet werden, um Code zwischen mehreren Produkten wiederzuverwenden / zu teilen; Laden bei Bedarf in den Prozessspeicher und kann bei Nichtgebrauch entladen werden; kann unabhängig vom Rest des Programms aktualisiert werden.
DLL-Nachteile : Auswirkungen auf die Leistung beim Laden der DLL und beim erneuten Basieren des Codes; Versionsprobleme ("dll hell")
Lib-Profis : Keine Auswirkungen auf die Leistung, da Code immer in den Prozess geladen und nicht neu basiert wird. Keine Versionsprobleme.
Lib cons : ausführbare Datei / Prozess "aufblähen" - Der gesamte Code befindet sich in Ihrer ausführbaren Datei und wird beim Prozessstart geladen. Keine Wiederverwendung / Weitergabe - jedes Produkt hat eine eigene Kopie des Codes.
Neben den technischen Auswirkungen von statischen und dynamischen Bibliotheken (statische Dateien bündeln alles in einer großen binären oder dynamischen Bibliothek, die die gemeinsame Nutzung von Code zwischen mehreren verschiedenen ausführbaren Dateien ermöglicht) gibt es rechtliche Auswirkungen .
Wenn Sie beispielsweise LGPL-lizenzierten Code verwenden und statisch mit einer LGPL-Bibliothek verknüpfen (und somit eine große Binärdatei erstellen), wird Ihr Code automatisch zu Open Sourced- LGPL-Code ( kostenlos wie in Freiheit) . Wenn Sie eine Verknüpfung zu freigegebenen Objekten herstellen, müssen Sie nur die Verbesserungen / Fehlerbehebungen vornehmen, die Sie an der LGPL-Bibliothek selbst vorgenommen haben.
Dies wird zu einem weitaus wichtigeren Problem, wenn Sie beispielsweise entscheiden, wie Sie Ihre mobilen Anwendungen kompilieren möchten (bei Android haben Sie die Wahl zwischen statisch und dynamisch, bei iOS nicht - es ist immer statisch).
C ++ - Programme werden in zwei Phasen erstellt
Die statische Bibliothek (.lib) ist nur ein Bündel von .obj-Dateien und daher kein vollständiges Programm. Es hat nicht die zweite (Verknüpfungs-) Phase des Erstellens eines Programms durchlaufen. DLLs hingegen sind wie Exes und daher vollständige Programme.
Wenn Sie eine statische Bibliothek erstellen, ist diese noch nicht verknüpft. Daher müssen Benutzer Ihrer statischen Bibliothek denselben Compiler verwenden, den Sie verwendet haben (wenn Sie g ++ verwendet haben, müssen sie g ++ verwenden).
Wenn Sie stattdessen eine DLL erstellt haben (und diese korrekt erstellt haben ), haben Sie ein vollständiges Programm erstellt, das alle Benutzer verwenden können, unabhängig davon, welchen Compiler sie verwenden. Es gibt jedoch verschiedene Einschränkungen beim Exportieren aus einer DLL, wenn Cross-Compiler-Kompatibilität gewünscht wird.
consumers of your static library will have to use the same compiler that you used
Wenn die statische Bibliothek eine C ++ - Bibliothek verwendet, z #include <iostream>
.
$$:~/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]>
Eine statische Bibliothek wird in den Client kompiliert. Eine .lib wird zur Kompilierungszeit verwendet und der Inhalt der Bibliothek wird Teil der konsumierenden ausführbaren Datei.
Eine dynamische Bibliothek wird zur Laufzeit geladen und nicht in die ausführbare Client-Datei kompiliert. Dynamische Bibliotheken sind flexibler, da mehrere ausführbare Clientdateien eine DLL laden und ihre Funktionalität nutzen können. Dies reduziert auch die Gesamtgröße und Wartbarkeit Ihres Client-Codes auf ein Minimum.
Sie sollten sorgfältig über Änderungen im Laufe der Zeit, Versionierung, Stabilität, Kompatibilität usw. nachdenken.
Wenn es zwei Apps gibt, die den gemeinsam genutzten Code verwenden, möchten Sie diese Apps zwingen, sich gemeinsam zu ändern, falls sie miteinander kompatibel sein müssen? Dann benutze die DLL. Alle Exes verwenden denselben Code.
Oder möchten Sie sie voneinander isolieren, damit Sie eine ändern und sicher sein können, dass Sie die andere nicht gebrochen haben? Verwenden Sie dann die statische Bibliothek.
DLL Hölle ist, wenn Sie wahrscheinlich eine statische Bibliothek verwendet haben sollten, aber Sie haben stattdessen eine DLL verwendet, und nicht alle Exes sind damit kompatibel.
Eine statische Bibliothek muss mit der endgültigen ausführbaren Datei verknüpft sein. es wird Teil der ausführbaren Datei und folgt ihr, wohin sie auch geht. Bei jeder Ausführung der ausführbaren Datei wird eine dynamische Bibliothek geladen, die als DLL-Datei von der ausführbaren Datei getrennt bleibt.
Sie würden eine DLL verwenden, wenn Sie die von der Bibliothek bereitgestellten Funktionen ändern möchten, ohne die ausführbare Datei erneut verknüpfen zu müssen (ersetzen Sie einfach die DLL-Datei, ohne die ausführbare Datei ersetzen zu müssen).
Sie würden eine statische Bibliothek verwenden, wenn Sie keinen Grund haben, eine dynamische Bibliothek zu verwenden.
Ulrich Dreppers Artikel über " Wie man gemeinsam genutzte Bibliotheken schreibt " ist auch eine gute Ressource, in der detailliert beschrieben wird, wie gemeinsam genutzte Bibliotheken am besten genutzt werden können oder was er als "Dynamic Shared Objects" (DSOs) bezeichnet. Es konzentriert sich mehr auf gemeinsam genutzte Bibliotheken im ELF- Binärformat, aber einige Diskussionen eignen sich auch für Windows-DLLs.
Lesen Sie diesen Artikel von Sun, um eine hervorragende Diskussion zu diesem Thema zu erhalten .
Es geht um alle Vorteile, einschließlich der Möglichkeit, zwischengeschaltete Bibliotheken einzufügen. Weitere Einzelheiten zum Einfügen finden Sie in diesem Artikel hier .
Der Kompromiss, den Sie (in einem großen Projekt) eingehen, liegt in der anfänglichen Ladezeit. Die Bibliotheken werden zu dem einen oder anderen Zeitpunkt verknüpft. Die Entscheidung, die getroffen werden muss, ist, dass die Verknüpfung lange genug dauert, bis der Compiler sie benötigt um die Kugel zu beißen und es vorne zu tun, oder kann der dynamische Linker es beim Laden tun.
Wenn Ihre Bibliothek von mehreren ausführbaren Dateien gemeinsam genutzt werden soll, ist es häufig sinnvoll, sie dynamisch zu gestalten, um die Größe der ausführbaren Dateien zu verringern. Ansonsten machen Sie es auf jeden Fall statisch.
Die Verwendung einer DLL hat mehrere Nachteile. Es gibt zusätzlichen Aufwand für das Laden und Entladen. Es gibt auch eine zusätzliche Abhängigkeit. Wenn Sie die DLL so ändern, dass sie nicht mehr mit Ihren ausführbaren Dateien kompatibel ist, funktionieren diese nicht mehr. Wenn Sie dagegen eine statische Bibliothek ändern, sind Ihre kompilierten ausführbaren Dateien, die die alte Version verwenden, nicht betroffen.
Wenn die Bibliothek statisch ist, wird der Code zum Zeitpunkt der Verknüpfung mit Ihrer ausführbaren Datei verknüpft. Dies macht Ihre ausführbare Datei größer (als wenn Sie den dynamischen Weg gegangen wären).
Wenn die Bibliothek dynamisch ist, werden zur Verknüpfungszeit Verweise auf die erforderlichen Methoden in Ihre ausführbare Datei integriert. Dies bedeutet, dass Sie Ihre ausführbare Datei und die dynamische Bibliothek versenden müssen. Sie sollten auch überlegen, ob der gemeinsame Zugriff auf den Code in der Bibliothek eine sichere, bevorzugte Ladeadresse ist.
Wenn Sie mit der statischen Bibliothek leben können, gehen Sie mit der statischen Bibliothek.
Wir verwenden viele DLLs (> 100) in unserem Projekt. Diese DLLs sind voneinander abhängig und daher haben wir uns für die Einrichtung der dynamischen Verknüpfung entschieden. Es hat jedoch die folgenden Nachteile:
Vielleicht war ein besseres Setup, alles zu einer statischen Bibliothek zu machen (und deshalb haben Sie nur eine ausführbare Datei). Dies funktioniert nur, wenn keine Codeduplizierung stattfindet. Ein Test scheint diese Annahme zu stützen, aber ich konnte kein offizielles MSDN-Zitat finden. So machen Sie zum Beispiel 1 exe mit:
Der Code und die Variablen von shared_lib2 sollten in der endgültigen zusammengeführten ausführbaren Datei nur einmal vorhanden sein. Kann jemand diese Frage unterstützen?
Statische Bibliotheken sind Archive, die den Objektcode für die Bibliothek enthalten, wenn sie mit einer Anwendung verknüpft sind, wird dieser Code in die ausführbare Datei kompiliert. Freigegebene Bibliotheken unterscheiden sich darin, dass sie nicht in die ausführbare Datei kompiliert werden. Stattdessen durchsucht der dynamische Linker einige Verzeichnisse nach den benötigten Bibliotheken und lädt diese dann in den Speicher. Mehr als eine ausführbare Datei kann dieselbe gemeinsam genutzte Bibliothek gleichzeitig verwenden, wodurch die Speichernutzung und die Größe der ausführbaren Datei verringert werden. Es gibt dann jedoch mehr Dateien, die mit der ausführbaren Datei verteilt werden müssen. Sie müssen sicherstellen, dass die Bibliothek auf dem Verwendungssystem installiert ist, auf dem der Linker sie finden kann. Durch statische Verknüpfung wird dieses Problem behoben, es wird jedoch eine größere ausführbare Datei erstellt.
Wenn Sie nur an eingebetteten Projekten oder speziellen Plattformen arbeiten, sind statische Bibliotheken der einzige Weg, und auch das Kompilieren in Ihre Anwendung ist oftmals weniger mühsam. Auch Projekte und Makefiles, die alles enthalten, machen das Leben glücklicher.
Ich würde eine allgemeine Faustregel geben: Wenn Sie eine große Codebasis haben, die alle auf Bibliotheken niedrigerer Ebenen (z. B. einem Utils- oder Gui-Framework) basiert, die Sie in besser verwaltbare Bibliotheken aufteilen möchten, werden diese zu statischen Bibliotheken. Dynamische Bibliotheken kaufen Ihnen eigentlich nichts und es gibt weniger Überraschungen - es wird zum Beispiel nur eine Instanz von Singletons geben.
Wenn Sie eine Bibliothek haben, die vollständig vom Rest der Codebasis getrennt ist (z. B. eine Bibliothek eines Drittanbieters), sollten Sie sie zu einer DLL machen. Wenn die Bibliothek LGPL ist, müssen Sie aufgrund der Lizenzbedingungen möglicherweise trotzdem eine DLL verwenden.