【问题标题】:Confusion with including header files包含头文件的困惑
【发布时间】:2011-12-03 05:26:02
【问题描述】:

当我包含一个头文件时,比方说,

//myheader.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

//....

#endif

进入,

//mycpp1.cpp
#include "myheader.h"

我被告知的是,当 mycpp1.cpp 包含 myheader.h 时,MY_HEADER_H 被定义,因此任何再次包含它的尝试都将导致错误。

现在,如果我想将它包含在 mycpp2.cpp 中。

//mpcpp2.cpp
#include "myheader.h"

它会被包含,还是在第一次包含时使用相同的声明?

【问题讨论】:

    标签: c++ include-guards


    【解决方案1】:

    每个文件的预处理器定义是分开的。因此,如果您将#includemyheader.h 分成两个单独的 .cpp 文件,它将被包含两次,而不是一次。每个 .cpp 一个。

    【讨论】:

    • 这就是我的猜测......有什么办法可以检查它是否被包含两次?
    • @user974191 你可以在预处理器处理完文件后查看文件,但你为什么要这样做?
    • 你是指目标文件吗?没有什么只是想了解发生了什么。
    • @user974191 没有。您可能知道,预处理器在编译之前获取每个文件并替换#defines,将#includes 替换为#included 等文件的内容。如果包含文件,您可以在该文件的#include 保护之外放置注释,然后查看预处理器的输出并查看该注释出现了多少次。所以对于 gcc,你会做 gcc -E file.cpp 这会给你预处理器的输出,你可以计算它出现的次数。
    • @user974191 如何查看预处理器的输出将因每个编译器/平台而异。就像我对 gcc 所说的那样,它有 -E 标志,对于 Visual Studio,它可能是一个项目设置。
    【解决方案2】:

    预处理器定义对于当前编译单元是本地的。

    当然有复杂的情况,但重点是:

    尝试将 .cpp(源)文件视为不同的实体。 如果你不做真正奇怪的事情,那么 如果您删除所有 .cpp 文件,除了您打扰的文件, 你仍然可以编译,因为在编译阶段不需要 对于定义,您只关心事物的名称(声明)。

    所以,一次编译N个源文件本质上是这样的:

    [ *.H + SOURCE1.CPP ] --> SOURCE1.O
    [ *.H + SOURCE2.CPP ] --> SOURCE2.O
    ...
    [ *.H + SOURCEN.CPP ] --> SOURCEN.O
    

    每一行都是一个不同的编译单元,它 将 SourceX.CPP 和包含的标头呈现到 Object 文件中。 所以我们得到了 N 个单独的东西。

    这样,如果您不更改公共标题,那么您不必 重新编译未修改的源文件。当然,如果你修改了一个源文件, 你必须重新编译它。最后,如果你修改了一个公共标题,那么你有 重新编译包含它的每个源文件。 这里不得不提一下,在编译阶段之前,所有 #inlude "filename.ext" 行(预处理器指令)将被替换 包含 filename.ext 文件的确切内容,无论它是什么。

    然后链接是一个不同的问题,在那个阶段 目标是从这 N 个目标文件中创建一个文件。 (我再说一遍,这是个简单的例子)

    这是链接:

    [ SOURCE1.O + SOURCE2.O + ... + SOURCEN.O ]  --> EXECUTABLE.FILE
    

    将受影响的目标文件想象成一袋值和 算法(函数定义)。例如,袋子里的某个地方一定有一个 main 函数(定义),所以链接器肯定会知道什么 执行程序时要做的事情。

    希望你明白

    猜猜会发生什么,如果将全局函数的定义写入头文件, 然后将它包含在两个单独的编译单元中,然后尝试链接它们。

    答案:链接器错误 - 多个定义,因为您可以单独编译它们。

    【讨论】:

      【解决方案3】:

      如果MY_HEADER_H只是定义在mycpp1.cpp中,头文件会包含在mycpp2.cpp中

      诀窍的真正用途是:

      header1.h 包括 header2.h
      因此,当您在 mycpp.cpp 中同时包含 header1.hheader2.h 时, header2.h 如果您不这样做,将被包含两次。

      【讨论】:

        【解决方案4】:

        正如其他人所说,每个 .cpp 文件包含一次头文件。但我还想提一下这样做的全部目的:

        #ifndef MY_HEADER_H
        #define MY_HEADER_H
        // ..
        #end if
        

        是为了防止这引起问题

        mycpp1.cpp

        #include "myheader.h"
        #include "myheader.h"
        // ...
        

        我开始时遇到的另一件事是,当您将头文件包含在多个 .cpp 文件中并在其中定义了全局变量时...这就是我要解决的问题...

        myheader.h

        #ifdef MY_HEADER_H
        #define MY_HEADER_H
        
        #ifdef GLOBAL_EXTERN
        extern int nGlobalInt;
        #else
        int nGlobalInt = 282;
        #endif
        
        #endif
        

        mycpp1.cpp

        #include "myheader.h"
        // ...
        
        cout << nGlobalInt;
        

        mycpp2.cpp

        #define GLOBAL_EXTERN
        #include "myheader.h"
        // ...
        
        cout << nGlobalInt;
        

        两个 cpp 文件都将打印 282

        【讨论】:

        • 如果没有#ifdef GLOBAL_EXTERN ...,您的编译器会出现重新定义错误。
        • 如果你在cpp文件的一个中定义nGlobalInt并去掉GLOBAL_EXTERN会更直接。 extern 充当该变量实例的前向声明。
        • @Victor T.,同意...但是对于这个问题的上下文,我认为这个例子会更好。
        【解决方案5】:

        它只会被包含一次。指令 MY_HEADER_H 将在第一次包含时定义,随后对#include myheader.h 的所有尝试都将无效。

        预处理器指令适用于所有文件。否则,例如,在 myheader.h 中声明的每个类都将被重新定义。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-29
          • 1970-01-01
          • 2019-11-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多