Answers:
静态库会增加二进制文件中代码的大小。它们始终会被加载,并且您使用的任何编译版本的代码都是将运行的代码版本。
动态库分别存储和版本控制。如果更新被认为与原始版本二进制兼容,则有可能加载的动态库版本不是代码随附的原始版本。
此外,动态库不一定要加载-它们通常在首次调用时就加载-可以在使用同一库的组件之间共享(多个数据加载,一个代码加载)。
大多数时候,动态库被认为是一种更好的方法,但是最初它们有一个重大缺陷(谷歌DLL地狱),但最近的Windows操作系统(尤其是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)中的一个例子是一个被标记为位于特定地址的中断处理程序。您还需要将中断处理程序标记为入口点,以确保不会被丢弃。
这样做的另一个结果是,静态库可能包含由于无法解析的引用而完全无法使用的目标文件,但是在您从这些目标文件中引用函数或变量之前,它不会导致链接器错误。编写库后很可能会发生这种情况。
调试符号:您可能希望为每个静态库使用一个单独的PDB,或者可能希望将调试符号放置在目标文件中,以便将它们导入DLL / EXE的PDB中。Visual C ++文档介绍了必要的选项。
RTTI:type_info
如果将单个静态库链接到多个DLL,则可能会导致同一类具有多个对象。如果您的程序假定该type_info
数据是“单个”数据,并使用&typeid()
或type_info::before()
,则可能会得到不良的结果。
库是捆绑在应用程序可执行文件中的代码单位。
dll是可执行代码的独立单元。仅当对该代码进行调用时才将其加载到进程中。一个dll可以由多个应用程序使用,并可以在多个进程中加载,同时在硬盘驱动器上仍然只有一个代码副本。
DLL专家:可用于在多个产品之间重用/共享代码;按需加载到过程内存中,并且在不需要时可以卸载;可以独立于程序的其余部分进行升级。
DLL缺点:DLL加载和代码变基对性能的影响;版本问题(“ dll地狱”)
易于使用的库:不会对性能造成影响,因为代码始终在过程中加载并且不会重新构建;没有版本问题。
缺点:可执行文件/进程“膨胀”-所有代码都在可执行文件中,并在进程启动时加载;禁止重复使用/共享-每个产品都有自己的代码副本。
C ++程序分两个阶段构建
静态库(.lib)只是一堆.obj文件,因此不是一个完整的程序。它尚未经历构建程序的第二个(链接)阶段。另一方面,Dll与exe类似,因此是完整的程序。
如果您构建静态库,那么它尚未链接,因此静态库的使用者将必须使用与您使用的编译器相同的编译器(如果您使用g ++,则他们将必须使用g ++)。
相反,如果您构建了一个dll(并正确地构建了它),那么您已经构建了一个完整的程序,无论使用什么编译器,所有使用者都可以使用。但是,如果需要跨编译器兼容性,则从dll导出有一些限制。
consumers of your static library will have to use the same compiler that you used
如果静态库使用C ++库,例如#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]>
必须将静态库链接到最终的可执行文件中。它成为可执行文件的一部分,并随处可见。每次执行可执行文件时,都会加载一个动态库,并且该动态库与DLL文件保持独立于可执行文件。
如果希望能够更改库提供的功能而不必重新链接可执行文件(只需替换DLL文件,而不必替换可执行文件),则可以使用DLL。
每当您没有理由使用动态库时,都将使用静态库。
我们在项目中使用了很多DLL(> 100)。这些DLL相互依赖,因此我们选择了动态链接的设置。但是,它具有以下缺点:
也许更好的设置是使所有内容都成为静态库(因此,您只有一个可执行文件)。仅当没有代码重复发生时,此方法才有效。测试似乎支持该假设,但是我找不到MSDN的官方报价。例如,使用以下命令制作1个exe:
shared_lib2的代码和变量在最终合并的可执行文件中应该只出现一次。谁能支持这个问题?
我有一个一般的经验法则,如果您有一个很大的代码库,它们都建立在较低级库(例如Utils或Gui框架)的基础上,您希望将这些库划分为更易于管理的库,然后将它们设置为静态库。动态库实际上并不会给您买任何东西,惊喜也很少-例如,只有一个单例实例。
如果您拥有一个与其余代码库完全独立的库(例如,第三方库),则可以考虑使其成为dll。如果该库是LGPL,由于许可条件,您可能仍需要使用dll。