C++ 编程/编译器/链接器/库
库允许在程序中重用现有代码。库类似于程序,但它们不依赖于main()来完成工作,而是调用库提供的特定函数来完成工作。函数提供了被编写程序和被使用的库之间的接口。这个接口被称为应用程序编程接口或 API。
库应该并且倾向于特定于领域,以便允许在应用程序之间具有更高的可移植性,并提供扩展的专业化。不是特定于领域的库通常是仅包含头文件的发布,旨在进行静态链接,以便允许编译器和应用程序仅使用所需的代码片段。
- 什么是 API?
对于程序员来说,操作系统由其API定义。API代表应用程序编程接口。API包含应用程序程序可以与硬件或操作系统通信的所有函数调用,或任何提供给程序员一组接口的其他应用程序(即:库),以及相关数据类型和结构的定义。大多数API都在应用程序软件开发工具包(SDK)中定义,用于程序开发。
简单来说,API可以被认为是用户(或用户程序)将通过其与操作系统、硬件或其他程序进行交互的接口,使它们执行一项任务,这也可能导致获得结果消息。
- API 可以称为框架吗?
不,框架可以提供 API,但框架不仅仅是一个简单的 API。默认情况下,框架还定义了代码的编写方式,它是一组解决方案,甚至是类,这些解决方案作为一个整体解决了有限的一组相关问题的处理,不仅提供 API,还提供默认功能,设计良好的框架使其可互换用于类似的框架,努力提供相同的 API。
如文件组织部分所示,编译后的库包含预处理器包含的 C++ 头文件和链接器用于生成最终编译结果的二进制库文件。对于动态链接库,只有加载代码被添加到使用它们的编译中,库的实际加载是在运行时在内存中完成的。
程序可以使用两种形式的库,静态或动态,具体取决于程序员如何决定分发其代码,甚至由于第三方库使用的许可证,本书的静态和动态库部分将深入探讨这个主题。
超越标准库(如垃圾回收)的附加功能可以通过第三方库获得(通常是免费的),但请记住,第三方库不一定提供与标准库相同的普遍跨平台功能或符合 API 样式。它们存在的主要动机是避免重复造轮子,并使努力集中;几代程序员已经花费了大量的精力来编写安全且“可移植”的代码。
程序员预计会了解或至少对一些库有所了解。时间、一致性和扩展引用将使一些库从其他库中脱颖而出。一个值得注意的例子是备受尊敬的Boost 库集合,我们将在后面进行研究。
- 第三方库的许可证
程序员也可能受到外部库许可证要求的限制,他无法直接控制这些要求,例如在封闭源应用程序中使用GNU 通用公共许可证(GNU GPL)代码是不允许的,为了解决这个问题,FSF 提供了 GNU LGPL 许可证的形式,允许这种使用,但只允许以动态链接的形式,这反映了程序员必须注意和遵守的许多其他法律要求。
库有两种形式,源代码形式或编译/二进制形式。源代码形式的库必须先编译才能包含到另一个项目中。这将把库的 cpp 文件转换为 lib 文件。如果必须重新编译程序才能使用新版本的库,但不需要进行任何其他更改,则该库被称为源代码兼容。如果程序不需要修改和重新编译即可使用库的新版本,则该库被归类为二进制兼容。
使用静态二进制文件的优点
- 简化程序分发(更少的文件)。
- 代码简化(不需要动态库中所需的版本检查)。
- 只编译使用的代码。
使用静态二进制文件的缺点
- 浪费资源:生成更大的二进制文件,因为库被编译到可执行文件中。浪费内存,因为库不能在进程之间(取决于操作系统)共享(在内存中)。
- 程序将不会从库中的错误修复或扩展中获益,除非重新编译。
- 库的二进制/源代码兼容性
如果动态链接到该库的早期版本的程序继续使用该库的其他版本工作,则该库被称为二进制兼容。如果程序需要重新编译才能使用每个新版本的库,则该库被称为源代码兼容。
生成二进制兼容库有利于分发,但程序员更难维护。如果库仅是源代码兼容的,静态链接通常被认为是更好的解决方案,因为它不会给最终用户造成问题。
二进制兼容性省去了很多麻烦,并且表明该库已达到稳定状态。这使得为特定平台分发软件变得更容易。如果不确保发行版之间的二进制兼容性,人们将被迫提供静态链接的二进制文件。
- 仅包含头文件的库
关于库的另一个常见的区别是关于它们是如何分发的(关于结构和使用)。仅包含头文件的库被称为仅包含头文件的库。这通常意味着它们更简单,更易于使用,但是对于复杂的代码,这并不是理想的解决方案,它不仅会影响可读性,还会导致更长的编译时间。此外,由于内联而导致的最终结果,取决于编译器及其优化能力(或选项),可能会生成更大的二进制文件。这对于主要使用模板实现的库来说可能并不那么重要。仅包含头文件的库将始终包含实现的源代码,商业库很少见。
Boost 库用作示例库。
假设您已经解压缩并已构建 Boost 库的二进制部分,那么您必须执行以下步骤:
设置包含目录。这是包含头文件(.h/hpp)的目录,这些头文件描述了库接口
设置库目录。这是包含预编译库文件(.lib)的目录。
在附加依赖项中输入库文件名,以便使用库。
某些库(例如 Boost)使用自动链接来自动选择要链接的库文件,具体取决于包含的头文件。如果您的编译器支持自动链接,则无需为这些库手动选择库文件名。
对于动态加载(.dll)库,还需要将 DLL 文件放置在与可执行文件相同的文件夹中,或放置在系统 PATH 中。
库还必须使用与项目中使用的相同的运行时库进行编译。因此,许多库都以不同的版本提供,具体取决于它们是针对单线程还是多线程运行时以及调试还是发布运行时编译的,以及它们是否包含调试符号。