C/C++包括头文件顺序

Any*_*orn 265 c c++

应该指定哪些顺序包含文件,即将一个标题包含在另一个标题之前的原因是什么?

例如,系统文件,STL和Boost是在本地包含文件之前还是之后运行?

squ*_*art 265

我不认为有推荐的订单,只要它编译!令人讨厌的是,当某些标头需要首先包含其他标头时...这是标题本身的问题,而不是包含的顺序.

我个人的偏好是从本地到全球,每个子部分按字母顺序排列,即:

  1. h文件对应于此cpp文件(如果适用)
  2. 来自同一组件的标题,
  3. 来自其他组件的标题,
  4. 系统标题.

我的理由是,它应该证明每个标题(有一个cpp)都可以#include没有先决条件.其余的似乎从那里开始逻辑流动.

  • @Jon:我说这几乎是相反的!:-)我认为你的方法可以引入隐藏的依赖,比如myclass.cpp包含<string>然后<myclass.h>,没有办法在构建时捕获myclass.h本身可能依赖于字符串; 因此,如果稍后您或其他人,包括myclass.h但不需要字符串,您将收到一个错误,需要在cpp或标题本身中修复.但我很想知道,从长远来看,这是否是人们认为会更好的...你为什么不在你的提案中发表答案,我们会看到谁"胜出"?;-) (117认同)
  • 和你差不多,除了我从全局到本地,并且对应于源文件的标题没有得到特殊处理. (15认同)
  • @PaulJansen是的,我指的是推翻标准行为.这可能是偶然发生的,例如,打破ODR可能偶然发生.解决方案不是使用在发生此类事故时隐藏的做法,而是使用最有可能使其尽可能大声爆炸的做法,以便尽早发现并修复错误. (10认同)
  • @PaulJansen这是一个不好的做法,使用一种更有可能爆炸的技术是好的,这样可以修复不良做法而不是隐藏.全球FTW本地化 (7认同)
  • 根据Dave Abrahams的推荐,我此时使用的具体到一般排序.并且他注意到同样的原因,@ squelart照亮了缺失的标题包括来源,从本地到更一般.重要的是你比第三方和系统库更容易犯这些错误. (3认同)
  • @squelart - 我无法链接到它..因为这是几年前和他在一起的电话交谈. (2认同)
  • @Ben:我明白你的观点,但我不确定你的解决方案到底是否真的有帮助:系统标题本身依赖于它不应该(不太可能)的东西,或者错误的本地标题是搞砸了非常糟糕,它可能会影响其他代码,因此应该修复!如果我们坚持应该创建标题以便它们*可以包含在任何顺序中的原则,那么我仍然更喜欢我的解决方案,因为它更有可能更早地突出依赖性问题...你会有现实生活吗?包含系统标题的示例第1或第2是否必要? (2认同)
  • 在大多数情况下,我实际上是在这里做相反的事情.我首先放置STL标头,然后是boost,然后是所有其他第三方标头.之后,我将包含在我所在的组件/模块/库中.我这样做是为了使具有相似名称的宏和符号在本地级别发生冲突,并且不会导致STL/boost/etc中的错误.STL的定义优先于其他所有内容.我在STL上面唯一提到的是相应的H文件包含.这样我就可以确保我的H文件正确管理自己的头依赖项. (2认同)

Nat*_*ons 92

要记住的重要一点是,您的标头不应该依赖于首先包含的其他标头.确保这一点的一种方法是在任何其他标头之前包含您的标头.

"用C++思考"特别提到了这一点,引用了Lakos的"大规模C++软件设计":

通过确保组件的.h文件自行解析,可以避免潜在的使用错误 - 没有外部提供的声明或定义......包含.h文件作为.c文件的第一行确保没有关键部分.h文件中缺少组件物理接口固有的信息(或者,如果有的话,只要您尝试编译.c文件就会发现它).

也就是说,按以下顺序包括:

  1. 此实现的原型/接口标头(即与.cpp/.cc文件对应的.h/.hh文件).
  2. 根据需要,来自同一项目的其他标头.
  3. 来自其他非标准非系统库的标题(例如,Qt,Eigen等).
  4. 来自其他"几乎标准"库的标题(例如,Boost)
  5. 标准C++标头(例如,iostream,功能等)
  6. 标准C头文件(例如,cstdint,dirent.h等)

如果任何标题包含在此顺序中的问题,请修复它们(如果您的)或不使用它们.阻止不编写干净标头的库.

谷歌的C++风格指南认为几乎相反的,跟真的没有道理的; 我个人倾向于赞成Lakos方法.

  • 截至目前,[Google C++样式指南](http://google.github.io/styleguide/cppguide.html#Names_and_Order_of_Includes)建议首先包含相关的头文件,遵循Lakos的建议. (10认同)

pax*_*blo 48

我遵循两个简单的规则来避免绝大多数问题:

  1. 所有的头(实际上任何的源文件)应该包括他们需要什么.他们应该依赖于他们的用户,包括东西.
  2. 作为附件,所有标题都应该包含警戒,这样就不会因为过于雄心勃勃地应用上述规则1而多次包含它们.

我也遵循以下准则:

  1. 首先包含带有分界线的系统头(stdio.h等).
  2. 按逻辑分组.

换一种说法:

#include <stdio.h>
#include <string.h>

#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
Run Code Online (Sandbox Code Playgroud)

虽然,作为指导方针,这是一个主观的事情.另一方面,我严格执行规则,甚至提供包含警卫的"包装"头文件和分组包括如果一些讨厌的第三方开发人员不订阅我的愿景:-)

  • 为什么首先包含系统头?因为你的第一条规则,其他原因会更好. (17认同)
  • +1"所有标题(实际上任何源文件)都应该包含他们需要的东西.他们不应该依赖于他们的用户,包括事物." 然而,许多人依赖于这种隐式包含行为,例如,使用NULL并且不包括<cstddef>.尝试移植此代码并在NULL上获取编译错误时非常烦人(我现在只使用0的一个原因). (5认同)
  • @jhasse如果您在系统标头之前包含另一个项目内或第三方标头,则该其他标头中使用的功能测试宏(或类似的预处理器内容)可能会无意中破坏您的翻译单元。您应该完全按照您想要使用的方式包含您使用的系统标头,然后才包含其他内容,并希望其他内容经过精心设计,以便它不会因您对系统标头的使用而中断。这样,您就有希望注意到破坏内容的第一个包含内容是什么,并且应该修复该标头。所以我坚持谷歌的命令。 (3认同)
  • @jhasse我的意思是使用标准宏(例如_POSIX_SOURCE),而不是您自己的宏。几年前,我在同时使用 ncurses 和 opencv 时遇到了冲突,因为其中一个定义了一个宏,而另一个宏的名称有意义,并且东西损坏了,而且很难找到问题的根源。由于编译器在它发现的第一个不一致处开始抱怨,并且由于我们可以依赖系统标头(通常)比其他库标头具有更少的错误,并且其他库标头比项目标头更少,因此更容易找到错误,包括那个订单。 (2认同)

Mat*_* M. 21

把我自己的砖添加到墙上.

  1. 每个标题都需要自给自足,只有在首次包含至少一次时才能进行测试
  2. 不应该通过引入符号(宏,类型等)错误地修改第三方标题的含义

所以我通常会这样:

// myproject/src/example.cpp
#include "myproject/example.h"

#include <algorithm>
#include <set>
#include <vector>

#include <3rdparty/foo.h>
#include <3rdparty/bar.h>

#include "myproject/another.h"
#include "myproject/specific/bla.h"

#include "detail/impl.h"
Run Code Online (Sandbox Code Playgroud)

每个组用下一个空白行分隔:

  • 首先对应于此cpp文件的标题(健全性检查)
  • 系统标头
  • 第三方标头,按依赖顺序组织
  • 项目标题
  • 项目私有标题

另请注意,除了系统标头之外,每个文件都位于具有其命名空间名称的文件夹中,因为它更容易以这种方式跟踪它们.

  • 这样其他头文件不受它们的影响.这些系统头文件定义的内容(X包含和Windows包含的内容都很糟糕,`#define`会弄乱其他代码)并防止隐式依赖.例如,如果我们的代码库头文件`foo.h`真的依赖于`<map>`但是在`.cc`文件中使用它的地方,`<map>`恰好已经包含在内,我们可能不会注意.直到有人试图包含`foo.h`而不首先包括`<map>`.然后他们会生气. (2认同)

msa*_*ord 14

我很确定这在理智的世界中的任何地方都不是推荐的做法,但我喜欢将系统包含在文件名长度中,并在相同长度内排序.像这样:

#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Run Code Online (Sandbox Code Playgroud)

我认为在其他人之前包含你自己的标题是个好主意,以避免包含顺序依赖的耻辱.

  • 按_length_排序?疯狂! (36认同)
  • 我喜欢通过使用由第二个,第三个然后第一个字母组成的键来排序我的标题:-)所以矢量,集合,算法,功能为您的示例. (3认同)
  • 第一个+1。这实际上是有道理的,如果您需要用眼睛直观地定位文件中的标题,这比按字母顺序要好得多。 (2认同)

i_a*_*orf 14

我建议:

  1. 您正在构建的.cc模块的标头.(帮助确保项目中的每个标头都没有对项目中其他标头的隐式依赖关系.)
  2. C系统文件.
  3. C++系统文件.
  4. 平台/ OS /其他头文件(例如win32,gtk,openGL).
  5. 项目中的其他头文件.

当然,在可能的情况下,每个部分的字母顺序.

始终使用前向声明以避免#include头文件中不必要的s.

  • 按字母顺序排列是一种任意顺序,但很容易.您不必按字母顺序排列,但您必须选择一些顺序,以便每个人都能始终如一地进行排序.我发现它有助于避免重复并使合并更容易.如果您使用sublime文本,F5将为您订购. (7认同)
  • 我绝对是系统/库文件第一种方法的粉丝,原因是如果这些文件中的某些内容被重新定义,那么错误会出现在执行重新定义的文件中。否则,错误会出现在标准库一部分的文件中或您无法轻松更改的位置。当然,将模块包含在顶部与此不一致,但是拥有独立的标头也可以省去很多麻烦,而且很容易错过一些东西。 (3认同)

wil*_*ell 6

这不是主观的.确保标题不依赖#include于特定顺序.您可以确定包含STL或Boost标头的顺序无关紧要.


Agn*_*ian 5

首先包含与 .cpp 对应的标头...换句话说,应该在包含其他任何内容之前source1.cpp包含。source1.h我能想到的唯一例外是当使用带有预编译标头的 MSVC 时,在这种情况下,您必须在stdafx.h其他任何内容之前包含。

推理:将 包含source1.h在任何其他文件之前可确保它可以独立存在而无需依赖。如果source1.h稍后出现依赖项,编译器将立即提醒您将所需的前向声明添加到source1.h. 这反过来又确保了标头可以由其相关项以任何顺序包含。

例子:

源1.h

class Class1 {
    Class2 c2;    // a dependency which has not been forward declared
};
Run Code Online (Sandbox Code Playgroud)

源1.cpp

#include "source1.h"    // now compiler will alert you saying that Class2 is undefined
                    // so you can forward declare Class2 within source1.h
...
Run Code Online (Sandbox Code Playgroud)

MSVC 用户:我强烈建议使用预编译头。因此,将#include标准标头(以及其他永远不会更改的标头)的所有指令移至stdafx.h.