c++ 如何创建库?

2j4z5cfb  于 2023-05-08  发布在  其他
关注(0)|答案(6)|浏览(259)

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

#include <mypackage>

而不是:

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

这样我就不需要在每次需要这个“包”的时候都写一个makefile了。
更准确地说,我使用Linux。

xj3cbfub

xj3cbfub1#

一组CPP源(H文件和CPP文件)可以一起编译成一个“库”,然后可以在其他程序和库中使用。如何实现这一点的细节是特定于平台和工具链的,所以我把它留给您来发现细节。但是,我会提供一些链接,您可以阅读:
Creating a shared and static library with the gnu compiler [gcc]
Walkthrough: Creating and Using a Dynamic Link Library (C++)
库可以分为两种类型:源代码库和二进制库。也可以是这两种类型的混合体--一个库可以既是源库又是二进制库。源代码库就是:作为源代码分发的代码集合;通常是头文件。大多数Boost库都属于这种类型。二进制库被编译到一个包中,该包可由客户端程序在运行时加载。
即使在二进制库的情况下(显然在源库的情况下),也必须向库的用户提供一个头文件(或多个头文件)。这告诉客户端程序的编译器在库中查找什么函数等。库编写者经常做的是一个单一的主头文件,由库导出的所有内容的声明组成,客户端将#include该头文件。稍后,对于二进制库,客户端程序将“链接”到库,这将头中提到的所有名称解析为可执行地址。
在编写客户端头文件时,请记住复杂性。在许多情况下,您的一些客户可能只想使用您的库的某些部分。如果您编写一个包含库中所有内容的主头文件,则客户机的编译时间将不必要地增加。
处理这个问题的一个常用方法是为库中相关的部分提供单独的头文件。如果你把所有的Boost都看作是一个库,那么Boost就是一个例子。Boost是一个庞大的库,但如果您只需要regex功能,则只能#include regex相关的头文件才能获得该功能。如果你想要的只是正则表达式的东西,你不必包括Boost的所有内容。
在Windows和Linux下,二进制库可以进一步细分为两种类型:动态和静态。在静态库的情况下,库的代码实际上被“导入”(因为缺乏更好的术语)到客户端程序的可执行文件中。静态库是由您分发的,但只有在编译步骤中客户端才需要它。当你不想强迫你的客户端在他们的程序中分发额外的文件时,这很方便。它还有助于避免Dependancy Hell。另一方面,动态库不是直接“导入”到客户端程序中,而是由客户端程序在执行时动态加载。这既减少了客户端程序的大小,又可能减少了多个程序使用相同动态库的情况下的磁盘占用空间,但库二进制文件必须与客户端程序一起分发和安装。

polkgigr

polkgigr2#

在Linux上:
g++ FLAGS -shared -Wl,-soname,libLIBNAME.so.1-o libLIBNAME.VERSION OBJECT_FILES
其中,
FLAGS:典型标志(例如,-g、-Wall、-Wextra等)
LIBNAME:您的库的名称
OBJECT_FILES:编译cpp文件产生的对象文件
VERSION:您的库的版本

iaqfqrcu

iaqfqrcu3#

假设你的“file1.hpp”和“file2.hpp”等是紧密相关的,并且(几乎)总是一起使用,那么制作一个包含其他组件的includes的“mypacakge.h”是一个好主意(它本身并没有使它成为一个库-这是一个完全不同的过程)。
如果它们不是紧密相关和/或一起使用的,那么你不应该有这样一个“mega include”,因为它只会拖进一堆不需要的东西。
创建一个库需要构建一次代码,然后生成一个.lib文件或一个共享的librar(.dll或.so文件)。具体的步骤取决于您使用的是什么系统,我在这里解释有点太复杂了。
编辑:进一步解释:所有的C库实际上是一个库文件或共享库文件[沿着一些头文件,其中包含一些代码和使用库中代码所需的声明]。但是你要分别包含<iostream><vector>--如果把所有不同的C库头文件都包含在一个<allcpplibrary>中,那就太糟糕了,即使它涉及的输入要少得多。它被分割成几个部分,每个头文件只做一件事。因此,您可以从一个头文件中获得一个“完整”的集合,但不会有太多您实际上不需要的其他东西。

vptzau2j

vptzau2j4#

是也不是
您可以编写一个include-all头,这样#include "myLib.h"就足够了,因为您通过单个头包含了所有这些头。但是,这并不意味着单个include就足以将10个'.cpp'文件的内容自动链接到您的项目。您必须将它们编译成一个库,并将单个库(而不是所有的对象文件)链接到使用“myLib.h”的项目。库的二进制文件有静态库和动态库两种形式,对于静态库和动态库,文件通常分别命名为.lib.dll(windows)以及.a.so(linux)。
如何构建和链接这些库取决于你的构建系统,你可能想在网上找到这些术语。
一种替代方法是通过在头文件中定义所有函数来摆脱.cpp文件。这样你就不必链接额外的库,但这会增加构建时间,因为每次你直接或间接地将头文件包含到一个翻译单元中时,编译器都必须处理所有这些函数。

zy1mlcev

zy1mlcev5#

如果客户端需要所有10个头文件才能真正使用你的“包”(库),那是相当糟糕的界面设计。
如果一个客户端只需要 some 头文件,这取决于你的库的哪些部分正在被使用,让客户端包括适当的头文件,这样只会引入最小的标识符集。这有助于范围、模块化和编译时间。
如果其他方法都失败了,您可以创建一个“接口头”供外部使用,这与您在内部实际编译库时使用的不同。这将是一个得到安装,并从其他头的必要内容组成。(我仍然不认为你需要从你的库中的每个标题中的所有内容。
我不赞成萨尔加的解决方案。你要么有单独的标题,要么有一个整体的标题。提供单独的标题 * 加上 * 一个简单地包括其他标题的中心标题,这让我觉得布局很糟糕。
我不明白的是Makefiles在这方面发挥了多大的作用。头依赖性应该由Makefile /构建系统自动解决,即在这里,头文件如何布局并不重要。

dced5bon

dced5bon6#

您只需创建一个.h或.hpp文件

#ifndef MAIN_LIB_H
#define MAIN_LIB_H

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

#endif

因为ifndef的缘故,我将文件命名为main_lib.h,然后

#include "DIRECTORY PATH IF THERE IS ONE/main_lib.h"

在主文件中。如果您使用的是Visual Studio,则不需要任何其他功能。只需构建,然后按CTRL + F5。

相关问题