【问题标题】:How does #include work in C++?#include 如何在 C++ 中工作?
【发布时间】:2016-06-13 17:42:33
【问题描述】:

如果一个库包含在一个类头中,然后这个头包含在另一个类中,我是否必须再次包含该库?

例如:

#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”吗?

【问题讨论】:

  • B 是否使用来自&lt;someLibrary.h&gt; 的任何东西(即直接而不是通过A)?如果是这样,那么是的,“包括你使用的东西” - 它使代码库对包含更改更加健壮。
  • 一位 Youtuber 告诉编译器将复制和粘贴 .h 文件的内容并将其粘贴到您的 .cpp 文件中。在这种情况下,您可以查看是否需要包含您的内容。

标签: c++ include


【解决方案1】:

你不包括类或库,你只包括标题,这是一个文本操作(有点像预处理器完成的复制和粘贴)。

阅读有关C/C++ preprocessorGNU cpp 的更多信息。

您可以要求您的编译器向您显示源文件foo.cc 的预处理形式,例如使用g++ -Wall -C -E foo.cc(它会溢出到 stdout 预处理的表单上)

对于一个小型项目(例如小于 200KLOC),所有翻译单元只包含一个通用头文件是明智的(而且我认为拥有很多小头文件是一种坏习惯,所以我通常放一个以上每个头文件的类定义)。顺便说一句,这种(单个头文件)方法对precompiled headers 很友好。有些人更喜欢在单个标题中拥有几个自己的 #include-d 子标题。

请注意,大多数 C++ 标准头文件(如 &lt;map&gt;&lt;vector&gt;....)都带有大量文本,因此您不希望有很小的编译单元(在我的 Linux 系统上,#include &lt;vector&gt; 是拖了一万多行,所以后面只有几十行源代码对编译器来说效率低下)

还要注意,C++ 类定义通常有很多内联成员函数(实际上您希望在同一个头文件中提供该内联函数的主体)。所以 C++ 头代码往往相当大...

(有些人喜欢将单个头文件拆分为多个子标题,这些子标题总是放在一起)

【讨论】:

  • “对于小型项目(例如小于 200KLOC),所有翻译单元只包含一个通用头文件是明智的” o.O
  • @PreferenceBean: 你上面的o.O 是什么意思?
  • “那个(单个头文件)方法对预编译头文件很友好” 虽然技术上正确,但预编译头文件的惯用方法(或者我认为,无论如何)不是让它们污染您的代码库,而是 [有条件地] 使用命令行参数包含庞大的标头(并让标头保护在您的代码中忽略实际的 #includes)。这允许您将所有 PCH 逻辑保留在它所属的构建系统中:)
【解决方案2】:

不,#includes 是可传递的。

但是,如果您的第二个文件本身使用 someLibrary 中的符号,则重新包含标题是一种不错的方式。这样你就不会“希望和祈祷”你永远不会删除中间包含。如果每个源文件#includes 都包含它直接需要的所有内容,那么您的代码库将更加健壮。标头守卫可防止这是一种浪费的策略。

【讨论】:

    【解决方案3】:

    preprocessor #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
    }
    

    这种包含是递归的,所以如果一个头文件包含另一个头文件,那么另一个头文件也将被包含。

    预处理器生成的源文件称为translation unit,是编译器实际看到的。


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


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

    没有链接器库的库称为header only库。

    【讨论】:

      猜你喜欢
      • 2016-11-30
      • 2014-10-08
      • 2011-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-20
      相关资源
      最近更新 更多