理解问题:预编译的标头和包含用法

Posted

技术标签:

【中文标题】理解问题:预编译的标头和包含用法【英文标题】:Comprehension issue: Precompiled headers & include usage 【发布时间】:2015-08-09 15:16:07 【问题描述】:

我遇到了关于预编译头文件和#include 指令使用的理解问题。 所以我在这里得到了我的“stdafx.h”,并在其中包括了向量、iostream 和字符串。关联的“stdafx.cpp”只包含“stdafx.h”,这很清楚。

因此,如果我设计自己的头文件,例如使用向量或 iostream 中的“代码”,我必须包含头文件,因为当时编译器不知道声明。那么为什么这里的一些帖子(include stdafx.h in header or source file?)说,即使这个文件包含所需的声明,在其他头文件中包含“stdafx.h”也是不好的。载体?所以基本上直接包含向量或预编译的头文件都没有关系,两者都做同样的事情。

我当然知道,如果相关的源文件包含所需的头文件,我不必在另一个头文件中包含一个头文件,因为当时声明是已知的。好吧,这只有在头文件包含在某处时才有效。

所以我的问题是:我应该避免在任何源文件中包含预编译的头文件吗?为什么?而且我有点困惑,因为我正在阅读网络上的矛盾表达,我不应该在头文件中包含任何内容,或者它可以吗?包含在头文件中? 那么现在是什么?

【问题讨论】:

【参考方案1】:

这将是一个带有意图的笼统陈述。 Visual Studio 项目follows this general design 中 PCH 的典型设置,值得回顾。也就是说:

    设计您的头文件,就好像没有 PCH 主头文件一样。 永远不要在您的头文件中构建包含顺序的依赖项,您希望包含的源文件会在您的头文件之前完成。 尽管有 PCH 主标头(我稍后会谈到),但始终在源文件中包含自定义标头之前标准标头。这使您的自定义标头更有可能被正确定义,并且不依赖于包含源文件先前包含的某些标准头文件。 始终设置适当的包含守卫或编译指示以避免多重包含。它们对于正常工作至关重要。

PCH 主头文件不应包含在您的头文件中。在设计您的标头时,这样做的目的是包含 header 编译所需的所有内容(以及需要的内容)。如果一个包含源文件需要额外的包含来实现它,它可以在你的标题之后根据需要将它们拉入。

以下是我将如何设置在 .h 和 .cpp 文件中使用多个标准头文件的项目的示例。

myobject.h

#ifndef MYAPP_MYOBJECT_H
#define MYAPP_MYOBJECT_H

// we're using std::map and std::string here, so..
#include <map>
#include <string>

class MyObject

    // some code

private:
    std::map<std::string, unsigned int> mymap;
;

#endif

请注意,上面的头文件应该在它包含的任何 .cpp 中编译,使用或不使用 PCH。转到源文件...

myobject.cpp

// apart from myobject.h, we also need some other standard stuff...

#include "myobject.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <numeric>

// code, etc...

注意myobject.h 不希望你包含它所依赖的东西。它没有在标题中使用&lt;iostream&gt;&lt;algorithm&gt; 等;我们在这里使用它。

这是没有 PCH 的典型设置。现在我们添加 PCH 主机


添加 PCH 主标头

那么我们如何设置 PCH master-header 来加速这个东西呢?为了这个答案,我只处理标准头文件和第 3 方库头文件,它们不会随着项目开发而发生变化。您不会编辑&lt;map&gt;&lt;iostream&gt;(如果是,请检查一下您的头部)。总之……

    See this answer 了解在 Visual Studio 中通常如何配置 PCH。它显示了一个文件(通常是 stdafx.cpp)如何负责生成 PCH,然后您的其余源文件通过包含 stdafx.h 来使用所述 PCH。

    决定 PCH 中的内容。作为一般规则,您的 PCH 应该是这样配置的。把非易失性的东西放在那里,剩下的留给常规来源。我们正在使用许多系统头文件,这些将成为我们的 PCH 主机的选择。

    确保参与 PCH turbo-mode 的每个源文件都包括 PCH 主标头first,如 (1) 中的链接答案中所述。

    李>

所以,首先,PCH 主标头:

stdafx.h

#ifndef MYAPP_STDAFX_H
#define MYAPP_STDAFX_H

// MS has other stuff here. keep what is needed

#include <algorithm>
#include <numeric>
#include <iostream>
#include <fstream>
#include <map>
#include <string>

#endif

最后,配置为使用它的源文件然后执行此操作。所需的最小更改是:

更新:myobject.cpp

#include "stdafx.h" // <=== only addition
#include "myobject.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <numeric>

// code, etc...

注意我说的是最小。实际上,这些标准头文件都不再需要出现在 .cpp 中,因为 PCH 主机正在将它们拉入。换句话说,您可以这样做:

更新:myobject.cpp

#include "stdafx.h"
#include "myobject.h"

// code, etc...

是否选择取决于您。我更喜欢保留它们。是的,它可以延长源文件的预处理器阶段,因为它会拉入标头,运行到包含守卫,然后将所有内容都扔掉,直到最后的#endif。如果您的平台支持#pragma once(而 VS 支持),那几乎是无操作的。

但不要搞错:所有这一切中最重要的部分是标题myobject.h 没有更改根本,并且包含或知道,PCH 主报头。它不应该,也不应该如此建造。

【讨论】:

感谢您的详细解答。我现在一切都清楚了。【参考方案2】:

预编译头文件是一种缩短构建时间的方法。这个想法是编译器可以“预编译”头文件中的声明和定义,而不必再次解析它们。

以当今计算机的速度,预编译只对大型项目有意义。这些项目至少有超过 50k 行代码。 “意义”的定义通常需要几十分钟来构建。

围绕微软的stdafx.h 有很多问题。以我的经验,发现和解决问题所花费的精力和时间,使得这个功能对于较小的项目规模来说更加麻烦。我的构建设置大部分时间,我只编译几个文件;未更改的文件不会被编译。因此,我认为预编译的标头没有任何巨大的影响或好处。

【讨论】:

嗯,这是一个很好的说法,但没有回答我的问题。我的问题不仅仅是关于预编译头文件和使用它们的问题。我只是问我什么时候应该包含在我的头文件中,例如像 这样的东西,当关联的 .cpp 无论如何都包含矢量时,所以我不必将它包含在头文件中。但这里的问题是这种“方式”仅在我有关联的源文件时才有效,如果没有,例如所有都在标题中实现(内联)我必须在我的标题中包含头文件。【参考方案3】:

使用预编译头功能时,每个 .cpp 文件都必须以包含 stdafx.h 头开始。如果没有,则会导致编译器错误。因此,将包含放在某个头文件中是没有意义的。除非已首先包含 stdafx.h,否则无法包含该头文件。

【讨论】:

所以你的答案是,我不必在我设计的头文件中包含任何内容,因为我在实际头文件之前包含在 .cpp 文件 stdadx.h 中?好吧,只有在我有关联的源文件时才有效,头文件可以内联实现所有东西。 头文件必须#included在某个源文件中,否则根本不会编译。 是的,我知道这一点,所以您建议我不要在我的头文件中放置 #include 指令,因为当我将它包含在源文件中时,我应该包含所需的头文件有吗? @YannikK。绝对不。不要在编译单元中构建源文件包含顺序依赖项。您的标头应该从不假定包含的源文件将在拉入您的标头之前包含“正确的东西”。如果您的标头依赖于其他标头,则您的标头将其包含在标头中,句点。 @WhozCraig 谢谢,这是一个明确的答案。关于一般问题的“预编译头”部分的另一个问题:为什么我不应该在我的头文件中包含“stdafx.h”,即使它包含我在该头文件中需要的头文件?

以上是关于理解问题:预编译的标头和包含用法的主要内容,如果未能解决你的问题,请参考以下文章

iOS 和 OS X 目标的通用预编译标头

包含指令的预编译头文件

预编译标头 IntelliSense 错误

GCC 和预编译头文件

未能发出预编译的标头

预编译标准库头文件 - C++