C++中的#include是如何工作的?

12
如果一个库被包含在一个类头文件中,然后这个头文件被包含在另一个类中,我是否需要再次包含该库?
例如:
#ifndef A_H
#define A_H

#include<someLibrary.h>

class A{
  ...
}

#endif

然后另一个类包含A.h头文件。

#include<A.h>   //include class A
class B{
   ...
}

我是否需要在 B 类中包含 "someLibrary.h" 头文件?


5
B是否使用了<someLibrary.h>中的任何内容(直接使用而非通过A间接使用)? 如果是,则应该采用“引用即使用”的原则 - 这样可以使代码库更加稳健,以适应包含变化。 - Niall
一个Youtuber说编译器会复制粘贴.h文件的内容并将其粘贴到.cpp文件中。在这种情况下,您可以看到是否需要包含您的内容。 - SdSaati
3个回答

13

不,#include 是传递性的。

然而,如果您的第二个文件本身使用了 someLibrary 中的符号,重新包含头文件是良好的编程风格。这样你就不必"希望和祈祷",并且永远不会删除中间包含的内容。如果每个源代码文件都#include所需要的所有内容,那么您的代码库将更加健壮。头文件保护防止这成为一种浪费资源的策略。


11

预处理器中的 #include 指令的作用就像它的名字一样,它实际上会在指令位置包含该文件。

举个简单的例子:假设我们有两个文件,第一个头文件名为 a.h

// Our class
class A
{
    // Stuff for the class
};
// End of the class

接下来是一个源代码文件 a.cpp:

// Include header file
#include "a.h"
// Header file included

int main()
{
    // Code
}

预处理器生成一个看起来像 单个 文件的文件

// Include header file
// Our class
class A
{
    // Stuff for the class
};
// End of the class
// Header file included

int main()
{
    // Code
}

这种包含是递归的,因此如果一个头文件包含另一个头文件,则该另一个头文件也将被包含在内。

预处理器生成的源文件称为翻译单元,也是编译器实际看到的。


上述内容是现代预处理器工作原理的简化,虽然可以单独运行以创建经过预处理的源文件,但更常见的是预处理器是编译器的一部分,以简化解析过程。


你还应注意,你使用的术语不正确。一个库可以(并且通常)有一个或多个头文件,在编译您的源代码时使用。然后,库通常(但不总是)包含一个特殊的库文件,该文件与编译器创建的目标文件进行链接

没有链接库的库称为仅头文件库。


5
您不需要包含类或库,只需要包含头文件,这是一个文本操作(有点像预处理器执行的复制和粘贴)。
了解更多关于C/C++预处理器和GNU cpp的内容,请点击此处此处
您可以要求编译器显示源文件foo.cc的预处理形式,例如使用g++ -Wall -C -E foo.cc命令(它将在stdout上输出预处理的形式)。
对于小型项目(例如小于200KLOC),只有一个普遍的头文件被所有翻译单元包含是明智的(我认为拥有许多小的头文件是不好的习惯,因此我通常在一个头文件中放置多个类定义)。顺便说一下,这种(单个头文件)方法对于预编译头文件很友好。有些人喜欢在单个头文件中包含几个自己的#include-d子头文件。
请注意,大多数C++标准头文件(如<map><vector>....)带来了大量文本,因此您不希望有微小的编译单元(在我的Linux系统上,#include <vector>带来了超过一万行,因此仅有几十行源代码行之后效率很低)。
还请注意,C++类定义通常具有大量内联成员函数(实际上,您希望在同一个头文件中给出该内联函数的主体)。因此,C++头文件代码倾向于相当庞大... (有些人更喜欢将单个头文件分解为许多子头文件,这些子头文件总是一起被包含)

2
对于小型项目(例如少于200KLOC),只有一个公共头文件被所有翻译单元包含是明智的。 - Lightness Races in Orbit
1
@PreferenceBean:你上面的 o.O 是什么意思? - Basile Starynkevitch
“那种(单个头文件)方法对于预编译头非常友好。”虽然从技术上讲是正确的,但惯用的预编译头方法(至少我是这么认为的)不是让它们污染您的代码库,而是使用命令行参数[有条件地]包含庞大的头文件(并让头保护省略您代码中的实际#includes)。这样可以使您将所有PCH逻辑保留在其应该在的构建系统中 :) - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接