仅标头库的好处是什么?为什么要以与将实现放入单独文件相反的方式编写它?
仅标头库的好处是什么?为什么要以与将实现放入单独文件相反的方式编写它?
Answers:
在某些情况下,仅标头库是唯一的选择,例如在处理模板时。
拥有仅标头的库也意味着您不必担心可能在哪个平台上使用该库。分离实现时,通常会隐藏实现细节,并将库作为标头和库(lib
,dll
或.so
文件)的组合进行分发。当然,必须为您提供支持的所有不同操作系统/版本编译这些文件。
您也可以分发实现文件,但这对用户来说意味着额外的步骤-在使用库之前对其进行编译。
当然,这视情况而定。例如,仅标头库有时会增加代码大小和 编译时间。
仅标头库的优点:
仅标头库的缺点:
更大的目标文件。在某些源文件中使用的库中的每个内联方法也将在该源文件的已编译目标文件中获得弱符号,脱机定义。这减慢了编译器的速度,也减慢了链接器的速度。编译器必须生成所有这些膨胀,然后链接程序必须将其过滤掉。
更长的编译时间。除了上面提到的膨胀问题之外,编译将花费更长的时间,因为带有仅标头的库的标头本质上比编译的库大。对于使用该库的每个源文件,都将需要解析那些大的标头。另一个因素是,仅标头库中的那些标头文件必须具有#include
内联定义所需的标头以及将库构建为编译库时所需的标头。
更纠结的编译。由于仅标头库#include
需要更多的依赖,因此仅标头库具有更多的依赖项。更改库中某些关键功能的实现,您很可能需要重新编译整个项目。在已编译库的源文件中进行更改,您要做的就是重新编译一个库源文件,使用该新的.o文件更新已编译的库,然后重新链接该应用程序。
人类难以阅读。即使拥有最好的文档,图书馆的用户也常常不得不诉诸阅读图书馆的标头。仅标头库中的标头填充有实现细节,这些细节会妨碍您理解接口。使用已编译的库,您所看到的只是接口和关于实现功能的简要说明,而这通常就是您想要的。那才是您真正想要的。您不必知道实现细节即可知道如何使用该库。
detail
。
我知道这是一个旧线程,但是没有人提到ABI接口或特定的编译器问题。所以我想我会的。
这基本上是基于这样的概念:您编写一个带有标头的库以分发给人们,或者重用自己,而不是将所有内容都包含在标头中。如果您想重用头文件和源文件,并在每个项目中重新编译它们,那么这实际上并不适用。
基本上,如果您编译C ++代码并使用一个编译器构建一个库,那么用户尝试将该库与其他编译器或同一编译器的不同版本一起使用,则由于二进制不兼容,您可能会遇到链接器错误或奇怪的运行时行为。
例如,编译器供应商经常在版本之间更改其STL的实现。如果您的库中有一个接受std :: vector的函数,则它希望该类中的字节以编译库时的排列方式进行排列。如果在新的编译器版本中,供应商对std :: vector进行了效率改进,则用户的代码将看到可能具有不同结构的新类,并将该新结构传递到您的库中。一切都从那里走下坡路...这就是为什么建议不要跨越库边界传递STL对象的原因。C运行时(CRT)类型也是如此。
在谈论CRT时,您的库和用户的源代码通常需要链接到同一CRT。在Visual Studio中,如果您使用多线程CRT构建库,但是用户针对多线程调试CRT进行链接,那么您将遇到链接问题,因为您的库可能找不到所需的符号。我不记得它是哪个函数,但是对于Visual Studio 2015,Microsoft内置了一个CRT函数。突然,它不在标头中,而在CRT库中,因此希望在链接时找到它的库不再能做,这会产生链接错误。结果是这些库需要使用Visual Studio 2015重新编译。
如果您使用Windows API,但使用与库用户不同的Unicode设置进行构建,则还会出现链接错误或奇怪的行为。这是因为Windows API具有使用Unicode或ASCII字符串的函数,以及根据项目的Unicode设置自动使用正确类型的宏/定义。如果跨库边界传递的字符串类型错误,则在运行时会中断。或者,您可能会发现该程序最初没有链接。
对于跨其他第三方库(例如本征向量或GSL矩阵)跨库边界传递对象/类型的情况,这些情况也适用。如果第三方库在您编译库和用户编译其代码之间更改了它们的标头,那么事情将会中断。
基本上,为了安全起见,您可以跨库边界传递的唯一内容是类型和普通旧数据(POD)。理想情况下,任何POD应该采用您自己的标头中定义的结构,并且不依赖任何第三方标头。
如果您提供仅标头的库,则所有代码都将使用相同的编译器设置和相同的标头进行编译,因此许多此类问题都将消失(假设您和您的用户使用的第三部分库的版本与API兼容)。
但是,上面已经提到了负面因素,例如增加了编译时间。另外,您可能正在经营一家公司,因此您可能不想将所有源代码实现的详细信息交给所有用户,以防万一其中一个用户偷了它。
内联可以通过链接时间优化(LTO)完成
我想强调这一点,因为它降低了仅标头库的两个主要优点之一的值:“您需要在标头上进行内联定义”。
最小的具体示例如下所示:链接时优化和内联
因此,您只需传递一个标志,就可以在目标文件之间进行内联而不需要任何重构工作,而无需再在标头中保留定义。
LTO也可能有其自身的缺点:是否有理由不使用链接时间优化(LTO)?