Когда использовать динамические и статические библиотеки

при создании библиотеки классов в C++ вы можете выбрать между dynamic (.DLL) и статические (.lib) и библиотек. В чем разница между ними и когда уместно их использовать?

18 ответов


статические библиотеки увеличивают размер кода в двоичном файле. Они всегда загружаются, и любая версия кода, с которой вы скомпилировали, является версией кода, который будет запущен.

динамические библиотеки хранятся и версионные отдельно. Возможно, будет загружена версия динамической библиотеки, которая не была оригинальной, поставляемой с вашим кодом Если обновление считается двоичным, совместимым с оригиналом версия.

кроме того, динамические библиотеки не обязательно загружаются - они обычно загружаются при первом вызове-и могут совместно использоваться компонентами, использующими одну и ту же библиотеку (несколько загрузок данных, одна загрузка кода).

динамические библиотеки считались лучшим подходом большую часть времени, но первоначально у них был главный недостаток (Google DLL hell), который был устранен более поздними ОС Windows (в частности, Windows XP).


другие адекватно объяснили, что такое статическая библиотека, но я хотел бы указать на некоторые предостережения об использовании статических библиотек, по крайней мере, в Windows:

  • синглтоны: если что-то должно быть глобальным/статическим и уникальным, будьте очень осторожны с помещением его в статическую библиотеку. Если несколько библиотек DLL связаны с этой статической библиотекой, каждый из них получит свою собственную копию синглтона. Однако, если ваше приложение является одним EXE без пользовательские DLL, это не может быть проблемой.

  • удаление неферментированного кода: когда вы ссылаетесь на статическую библиотеку, только части статической библиотеки, на которые ссылается ваша DLL/EXE, будут связаны с вашей DLL / EXE.

    например, если mylib.lib содержит a.obj и b.obj и ваш DLL / EXE ссылается только на функции или переменные из a.obj все b.obj будет отброшен компоновщиком. Если b.obj содержит глобальные / статические объекты, их конструкторы и деструкторы не будут выполняться. Если эти конструкторы/деструкторы имеют побочные эффекты, вы можете быть разочарованы их отсутствием.

    аналогично, если статическая библиотека содержит специальные точки входа, вам может потребоваться позаботиться о том, чтобы они были фактически включены. Примером этого во встроенном программировании (хорошо, не Windows) будет обработчик прерываний, который помечен как находящийся по определенному адресу. Также необходимо отметить обработчик прерываний как entrypoint, чтобы убедиться, что он не будет отброшен.

    другим следствием этого является то, что статическая библиотека может содержать объектные файлы, которые полностью непригодны для использования из-за неразрешенных ссылок, но это не вызовет ошибки компоновщика, пока вы не ссылаетесь на функцию или переменную из этих объектных файлов. Это может произойти еще долго после того, как библиотека будет написана.

  • отладочных символов: вам может потребоваться отдельный PDB для каждой статической библиотеки, или вы можете хотите, чтобы символы отладки были помещены в объектные файлы, чтобы они были свернуты в PDB для DLL/EXE. Документация Visual C++ объясняет необходимые опции.

  • RTTI: вы можете в конечном итоге с несколькими type_info объекты для одного класса, если вы связываете одну статическую библиотеку в несколько библиотек DLL. Если ваша программа предполагает, что type_info это "синглтон" данных и использует &typeid() или type_info::before(), вы можете получить нежелательные и неожиданные результаты.


lib-это единица кода, которая входит в состав исполняемого файла приложения.

dll-это автономная единица исполняемого кода. Он загружается в процессе только тогда, когда вызов выполняется в этот код. Dll может использоваться несколькими приложениями и загружаться в нескольких процессах, имея при этом только одну копию кода на жестком диске.

Dll pros: смогите быть использовано для повторного пользования / кода доли между несколькими продуктов; нагрузка в отростчатой памяти по требованию и может быть выгружен, когда это не нужно; может быть обновлен независимо от остальной части программы.

Dll минусы: влияние на производительность загрузки dll и перебазирования кода; проблемы управления версиями ("dll hell")

Lib pros: нет влияния на производительность, поскольку код всегда загружается в процессе и не перезагружается; нет проблем с версиями.

Lib cons: исполняемый файл / процесс "bloat" - весь код находится в исполняемом файле и загружается при запуске процесса; нет повторного использования / совместного использования-каждый продукт имеет свою собственную копию кода.


помимо технических последствий статических и динамических библиотек (статические файлы связывают все в одну большую двоичную и динамическую библиотеки, которые позволяют обмениваться кодом между несколькими различными исполняемыми файлами), есть правовые последствия.

например, если вы используете лицензионный код LGPL и статически связываетесь с библиотекой LGPL (и, таким образом, создаете один большой двоичный файл), ваш код автоматически становится открытым исходным кодом (свободный как в свободе) код LGPL. Если вы ссылаетесь на общие объекты, вам нужно только LGPL улучшения / исправления ошибок, которые вы делаете в самой библиотеке LGPL.

Это становится гораздо более важной проблемой, если вы решаете, как скомпилировать вам мобильные приложения, например (в Android у вас есть выбор статического vs динамического, в 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]>

вы должны тщательно подумать об изменениях во времени, управлении версиями,стабильности, совместимости и т. д.

Если есть два приложения, которые используют общий код, вы хотите, чтобы заставить эти приложения, чтобы меняться вместе, если они должны быть совместимы друг с другом? Затем используйте dll. Все exe будут использовать один и тот же код.

или вы хотите изолировать их друг от друга, чтобы вы могли изменить один и быть уверены, что вы не сломали другой. Затем используйте статический lib.

DLL hell-это когда вы, вероятно, должны были использовать статический lib, но вместо этого вы использовали dll, и не все бывшие с ним совместимы.


программы на C++ построены в два этапа

  1. Compilation-создает объектный код (.параметр obj)
  2. Linking-создает исполняемый код (.exe или .dll файлы)

статическая библиотека (.lib) - это просто пучок .obj файлы и, следовательно, не является полной программой. Он не прошел второй (связывающий) этап построения программы. DLL, с другой стороны, похожи на exe и, следовательно, являются полными программами.

Если вы создаете статическую библиотеку, это еще не связан, и поэтому потребители вашей статической библиотеки должны будут использовать тот же компилятор, который вы использовали (если вы использовали g++, им придется использовать g++).

Если вместо этого вы построили dll (и построили его правильно), вы создали полную программу, которую могут использовать все потребители, независимо от того, какой компилятор они используют. Однако существует несколько ограничений на экспорт из dll, если требуется перекрестная совместимость компилятора.


статическая библиотека компилируется в клиент. Ля. lib используется во время компиляции, и содержимое библиотеки становится частью потребляющего исполняемого файла.

динамическая библиотека загружается во время выполнения, а не скомпилирована в исполняемый файл клиента. Динамические библиотеки более гибки, поскольку несколько исполняемых файлов клиента могут загружать DLL и использовать ее функциональность. Это также позволяет свести к минимуму общий размер и ремонтопригодность клиентского кода.


статическая библиотека должна быть связана с конечным исполняемым файлом; она становится частью исполняемого файла и следует за ним, куда бы он ни пошел. Динамическая библиотека загружается каждый раз при выполнении исполняемого файла и остается отдельной от исполняемого файла в виде DLL-файла.

вы бы использовали DLL, когда хотите иметь возможность изменять функциональность, предоставляемую библиотекой, без необходимости повторно связывать исполняемый файл (просто замените DLL-файл, без необходимости замены исполняемого файла папка.)

вы будете использовать статическую библиотеку, когда у вас нет причин использовать динамическую библиотеку.


статья Ульриха Дреппера о"Как писать общие библиотеки " также является хорошим ресурсом, который подробно описывает, как лучше всего использовать общие библиотеки или то, что он называет "динамическими общими объектами" (DSOs). Он больше фокусируется на общих библиотеках в эльф двоичный формат, но некоторые обсуждения подходят и для DLL Windows.


для отличного обсуждения этой темы прочтите в этой статье от Солнца.

Он входит во все преимущества, включая возможность вставки интерполирующих библиотек. Более подробно о разъединительного можно найти в вот эту статью.


на самом деле компромисс, который вы делаете (в большом проекте), находится в начальном времени загрузки, библиотеки будут связаны в то или иное время, решение, которое должно быть принято, займет достаточно много времени, чтобы компилятор должен укусить пулю и сделать это спереди, или динамический компоновщик может сделать это во время загрузки.


Если ваша библиотека будет совместно использоваться несколькими исполняемыми файлами, часто имеет смысл сделать ее динамической, чтобы уменьшить размер исполняемых файлов. В противном случае, определенно сделайте его статическим.

существует несколько недостатков использования dll. Есть дополнительные накладные расходы на погрузку и разгрузку. Существует также дополнительная зависимость. Если вы измените dll, чтобы сделать его несовместимым с вашими executalbes, они перестанут работать. С другой стороны, если вы измените статический библиотека, ваши скомпилированные исполняемые файлы, использующие старую версию, не будут затронуты.


Если библиотека статична, то во время ссылки код связан с вашим исполняемым файлом. Это делает ваш исполняемый файл больше (чем если бы вы пошли по динамическому маршруту).

Если библиотека динамическая, то во время ссылки ссылки на необходимые методы встроены в исполняемый файл. Это означает, что вы должны отправить исполняемый файл и динамическую библиотеку. Вы также должны рассмотреть вопрос о том, является ли общий доступ к коду в библиотеке безопасным, предпочтительным адресом загрузки среди других материал.

Если вы можете жить со статической библиотекой, перейдите в статическую библиотеку.


статические библиотеки-это архивы, содержащие объектный код библиотеки, если он связан с приложением, код которого компилируется в исполняемый файл. Общие библиотеки отличаются тем, что они не компилируются в исполняемый файл. Вместо этого динамический компоновщик ищет в некоторых каталогах нужную библиотеку(ы), а затем загружает ее в память. Более одного исполняемого файла могут одновременно использовать одну и ту же общую библиотеку, что снижает использование памяти и размер исполняемого файла. Тем не менее, есть затем больше файлов для распространения с исполняемым файлом. Вам нужно убедиться, что библиотека установлена в систему uses где-то, где компоновщик может ее найти, статическое связывание устраняет эту проблему, но приводит к большему исполняемому файлу.


Если ваша работа над встроенными проектами или специализированными платформами статические библиотеки-единственный способ пойти, также много раз они меньше хлопот для компиляции в ваше приложение. Также наличие проектов и makefile, которые включают все, делает жизнь счастливее.


мы используем много DLL (> 100) в нашем проекте. Эти DLL имеют зависимости друг от друга, и поэтому мы выбрали настройку динамической компоновки. Однако он имеет следующие недостатки:

  • медленный запуск (> 10 секунд)
  • DLL должны были быть версионными, так как windows загружает модули на уникальность имен. Собственные письменные компоненты в противном случае получили бы неправильную версию DLL (т. е. уже загруженную вместо собственной распределенной set)
  • оптимизатор может оптимизировать только в пределах границ DLL. Например, оптимизатор пытается разместить часто используемые данные и код рядом друг с другом, но это не будет работать через границы DLL

возможно, лучшей настройкой было сделать все статическая библиотека (и поэтому у вас есть только один исполняемый файл). Это работает только в том случае, если не происходит дублирования кода. Тест, похоже, поддерживает это предположение, но я не смог найти официальную цитату MSDN. Так например, сделайте 1 exe с:

  • exe использует shared_lib1, shared_lib2
  • shared_lib1 использовать shared_lib2
  • shared_lib2

код и переменные shared_lib2 должны присутствовать в конечном объединенном исполняемом файле только один раз. Может ли кто-нибудь поддержать этот вопрос?


Я бы дал общее эмпирическое правило, что если у вас есть большая кодовая база, все построено поверх библиотек нижнего уровня (например, Utils или GUI framework), которые вы хотите разделить на более управляемые библиотеки, то сделайте их статическими библиотеками. Динамические библиотеки на самом деле ничего не покупают, и сюрпризов меньше-например, будет только один экземпляр singletons.

Если у вас есть библиотека, которая полностью отделена от остальной части кода (например, третья сторона библиотека), затем рассмотрите возможность создания dll. Если библиотека LGPL, вам может потребоваться использовать dll в любом случае из-за условий лицензирования.