分发 Windows C++库:如何决定是创建静态库还是动态库

Distributing Windows C++ library: how to decide whether to create static or dynamic library?

本文关键字:创建 静态 决定 动态 C++ Windows 分发 何决定      更新时间:2023-10-16

我们一直在将Java和.NET API库转换为C++,并试图找出将编译版本分发给其他开发人员以用于其自定义应用程序的最佳方法。 它应该是静态库还是动态库?

我们需要为 Win32 和 Win64 创建(我想每个目标操作系统的调试和发布版本(。 鉴于我在尝试确保所有引用的库都匹配(/MT 与/MD(时遇到的所有挫败感,我想知道这里是否有一个决定可以简化其他开发人员的流程。

当我在静态库上运行dumpbin /all <static library file name> | find /i "msvc时,我没有看到任何运行时引用(不像我在.exe或.dll上做同样的事情(。 这是否表明运行时尚未链接,这是否使开发人员在开发和构建自己的应用时可以更灵活地制作/MT 或/MD?

哪种方法可以使开发人员的生活更轻松?

静态库更容易创建,但更难分发。 客户端程序员将将它们链接到他们的程序中,因此您的编译设置与他们兼容非常重要。 您必须分发至少 4 个版本,对应于 4 个不同的 CRT 版本(/MD、/MDd、/MT、/MTd(。 你需要把它乘以常用的Visual Studio版本的数量。 如果您不知道客户端程序员将使用什么,那么这可能是一个非常大的列表。

DLL

不是问题,您只需为导出的函数声明提供一个 .h,一个 .lib 作为 DLL 的导入库(没有代码,只包含名称(和.dll本身。

但是,很难为 DLL 创建可从任何 C 或 C++ 编译器使用的接口。 您不能公开任何标准的C++库类,例如返回 std::string 是行不通的。 不能创建任何分配需要由调用方释放的内存的函数。 通常不能跨边界引发异常。 执行任何这些操作都会导致客户端程序员很难诊断运行时问题,这是由不匹配的内存分配器和类对象布局差异引起的。 COM 对象模型就是此类接口的一个示例。

这不是静态库遇到的那种问题。 有些偶然的是,它们需要运行时和编译器版本匹配。 如果客户端程序员被一个错误的静态库所困,那么他们也会遇到所有这些问题。

静态和动态(共享(库都有显著的优势。 也许您的一种选择是分发两种类型的库,并让库用户决定使用哪种库。

就我自己而言,我通常使用静态库,除非我认为动态库的优点对项目有意义。

静态库的优点包括:

  • 一旦库的用户在编译时链接,库中的代码始终位于需要调用它的同一模块中。 所以没有DLL地狱,也没有并排地狱。 我无法计算我尝试在 Windows 上运行程序的次数,只是因为它失败了,因为我没有安装正确版本的 MSVC 运行时 (DLL(。 这真的是一种痛苦,如果可以避免,它会使每个人的生活更轻松。
  • 同样,库中代码的符号将最终出现在调用库的模块的.pdb中,而不是在您必须跟踪、复制等的另一个.pdb(.dll的.pdb(中。
  • 这是次要的,但对于静态库,只有链接它所需的函数/数据最终出现在可执行文件中。 而对于DLL,它是整个玉米卷饼。

动态库的优势之一

  • 明显的优势是,它允许在运行时替换库,甚至由最终用户替换,而无需重新链接。
  • 这在大多数环境中都很小,但如果许多可执行文件链接库,则拥有 DLL 意味着更少的磁盘空间,因为不会在每个可执行文件中重复相同的数据/代码。
  • 并不总是被理解的事情:如果库将同时由多个进程加载,将其作为 DLL 提供意味着 - 理想情况下 - 只读数据的一个副本(甚至是可写数据,直到并且除非它被特定进程写入(需要内存中。 运行时链接 DLL 的所有进程在内存中共享相同的字节。 共享的内存是总虚拟内存(RAM + 页面文件(是物理内存。 但是,这只是最好的情况 - 如果无法在两个进程中将 DLL 加载到同一个虚拟地址,则它们无法共享它。