49

假设我有 10 个 *.hpp 和 *.cpp 文件需要编译代码。我知道对于许多不同的代码,我需要这些相同的文件。我可以用那些允许我简单地编写的文件创建一个“包”:

#include <mypackage>

代替:

#include "file1.hpp"
#include "file2.hpp"
...
#include "file10.hpp"

每次我需要这个“包”时,我都不需要编写一个makefile。

更准确地说,我使用 Linux。

4

5 回答 5

79

一组 CPP 源(H 文件和 CPP 文件)可以一起编译到一个“库”中,然后可以在其他程序和库中使用。如何做到这一点的细节是平台和工具链特定的,所以我留给你去发现细节。但是,我将提供几个链接,您可以阅读:

使用 gnu 编译器 [gcc] 创建一个共享的静态库

演练:创建和使用动态链接库 (C++)

库可以分为两种类型:源代码库和二进制库。也可以是这两种类型的混合体——一个库既可以是源库,也可以是二进制库。源代码库就是:作为源代码分发的代码集合;通常是头文件。大多数 Boost 库都属于这种类型。二进制库被编译成一个包,可由客户端程序运行时加载。

即使在二进制库的情况下(显然在源库的情况下),也必须向库的用户提供一个头文件(或多个头文件)。这告诉客户端程序的编译器要在库中查找哪些函数等。库编写者经常做的是一个单一的主头文件,它由库导出的所有内容的声明组成,客户端将#include使用该头文件。稍后,在二进制库的情况下,客户端程序将“链接”到该库,这会将标头中提到的所有名称解析为可执行地址。

编写客户端头文件时,请牢记复杂性。在很多情况下,您的一些客户只想使用您图书馆的一些部分。如果您编写一个包含库中所有内容的主头文件,您的客户端编译时间将不必要地增加。

处理此问题的常用方法是为库的相关部分提供单独的头文件。如果您将所有 Boost 视为一个库,那么 Boost 就是一个例子。Boost 是一个庞大的库,但如果您想要的只是正则表达式功能,您只能使用#include与正则表达式相关的标头来获得该功能。如果你想要的只是正则表达式的东西,你不必包括所有的 Boost。

在 Windows 和 Linux 下,二进制库可以进一步细分为两种类型:动态和静态。在静态库的情况下,库的代码实际上被“导入”(因为没有更好的术语)到客户端程序的可执行文件中。静态库由您分发,但仅在编译步骤期间客户端需要。当您不想强迫您的客户必须使用他们的程序分发其他文件时,这很方便。它还有助于避免依赖地狱. 另一方面,动态库不是直接“导入”到客户端程序中,而是在客户端程序执行时动态加载。在多个程序使用同一个动态库的情况下,这既减少了客户端程序的大小,也可能减少了磁盘占用空间,但库二进制文件必须与客户端程序一起分发和安装。

于 2013-05-22T13:53:06.573 回答
5

在 Linux 上:

g++ FLAGS -shared -Wl,-soname,libLIBNAME.so.1 -o libLIBNAME.VERSION OBJECT_FILES

在哪里

FLAGS:典型标志(例如,-g、-Wall、-Wextra 等)

LIBNAME:你的图书馆的名字

OBJECT_FILES:编译 cpp 文件产生的对象文件

VERSION:库的版本

于 2013-05-22T13:59:50.233 回答
2

假设您的“file1.hpp”和“file2.hpp”等密切相关并且(几乎)总是一起使用,那么制作一个包含其他组件的“mypacakge.h”是一个好主意(它不它本身就变成了一个图书馆——这完全是一个不同的过程)。

如果它们不是密切相关和/或一起使用,那么你不应该有这样的“大型包含”,因为它只会拖入一堆不需要的东西。

制作一个库需要构建一次代码,然后生成一个 .lib 文件或一个共享库(.dll 或 .so 文件)。执行此操作的确切步骤取决于您使用的系统,我在这里解释有点太复杂了。

编辑:进一步解释:所有 C++ 库实际上是一个库文件或共享库文件[以及许多包含一些代码和使用库中代码所需的声明的头文件]。但是你分别包含<iostream>和包含<vector>- 将所有不同 C++ 库头文件中的所有内容都包含在一个中会变得非常糟糕<allcpplibrary>,即使它涉及的输入要少得多。它被分成几个部分,每个头文件做一件事。所以你从一个头文件中得到了一个“完整的”集合,而不是你实际上不需要的太多其他东西。

于 2013-05-22T13:42:18.257 回答
2

是和不是。

您可以编写一个包含所有标头,这样#include "myLib.h"就足够了,因为您通过单个标头包含所有这些标头。但是,这并不意味着单个包含足以将 10 个“.cpp”文件的内容自动链接到您的项目。您必须将它们编译成一个库并将该单个库(而不是所有目标文件)链接到使用“myLib.h”的项目。库二进制文件以静态和动态库的形式出现,这些文件通常分别命名为.lib.dll(windows)和.a.solinux)用于静态和动态库。

如何构建和链接此类库取决于您的构建系统,您可能想在网上查找这些术语。

一种替代方法是.cpp通过在头文件中定义所有函数来删除文件。这样您就不必链接额外的库,但它会以增加构建时间为代价,因为每次您将标头直接或间接包含到您的翻译单元之一时,编译器都必须处理所有这些函数。

于 2013-05-22T13:46:11.283 回答
1

如果客户需要所有十个标头来实际使用您的“包”(库),那是非常糟糕的界面设计。

如果客户端只需要一些头文件,这取决于正在使用的库的哪些部分,让客户端包含适当的头文件,因此只引入了最少的标识符集。这有助于范围、模块化和编译时间。

如果一切都失败了,您可以制作一个“接口头”供外部使用,这与您在内部用于实际编译库的不同。这将是安装的,并且包含来自其他标题的必要内容。(我仍然认为您不需要库中每个标头的所有内容。)

我不鼓励 Salgar 的解决方案。您要么有单独的标题,要么有一个整体的标题。提供单独的标题加上一个简单地包含其他标题的中央标题让我觉得布局很差。

明白的是Makefiles在多大程度上发挥了作用。头文件依赖项应该由您的 Makefile / 构建系统自动解决,即头文件的布局方式在这里无关紧要。

于 2013-05-22T13:41:45.017 回答