C / C ++包括头文件顺序
应该指定哪些顺序包含文件,即将一个标题包含在另一个标题之前的原因是什么?
例如,系统文件,STL和Boost是在本地包含文件之前还是之后?
我不认为有推荐的订单,只要它编译! 令人烦恼的是,当某些标题需要首先包含其他标题时…这是标题本身的问题,而不是包含的顺序。
我个人的偏好是从本地到全球,每个子部分按字母顺序排列,即:
- h文件对应于此cpp文件(如果适用)
- 来自同一组件的标头,
- 来自其他组件的标题,
- 系统标头。
我的理由是,它应该certificate每个标题(有一个cpp)可以是#include
d而没有先决条件。 其余的似乎从那里逻辑流动。
要记住的重要一点是,您的标头不应该依赖于首先包含的其他标头。 确保这一点的一种方法是在任何其他标头之前包含您的标头。
“用C ++思考”特别提到了这一点,引用了Lakos的“大规模C ++软件设计”:
通过确保组件的.h文件自行解析,可以避免潜在的使用错误 – 没有外部提供的声明或定义……包含.h文件作为.c文件的第一行确保没有关键部分.h文件中缺少组件物理接口固有的信息(或者,如果有的话,只要您尝试编译.c文件就会发现它)。
也就是说,按以下顺序包括:
- 此实现的原型/接口标头(即与.cpp / .cc文件对应的.h / .hh文件)。
- 根据需要,来自同一项目的其他标头。
- 来自其他非标准非系统库的标头(例如,Qt,Eigen等)。
- 来自其他“几乎标准”库的标题(例如,Boost)
- 标准C ++标头(例如,iostream,function等)
- 标准C头(例如,cstdint,dirent.h等)
如果任何标题包含在此顺序中的问题,请修复它们(如果您的)或不使用它们。 阻止不编写干净标头的库。
谷歌的C ++风格指南 几乎反过来说,根本没有任何理由; 我个人倾向于赞成Lakos方法。
我遵循两个简单的规则来避免绝大多数问题:
- 所有标题(实际上是任何源文件)都应包含所需内容。 他们不应该依赖包括东西在内的用户。
- 作为一个附件,所有标题都应该包含警戒,这样就不会因为过于雄心勃勃地应用上述规则1而多次包含它们。
我也遵循以下准则:
- 首先包含系统头(stdio.h等),并带有分界线。
- 按逻辑分组。
换一种说法:
#include #include #include "btree.h" #include "collect_hash.h" #include "collect_arraylist.h" #include "globals.h"
虽然,作为指导方针,这是一个主观的事情。 另一方面,我严格执行规则,甚至提供包含警卫和分组的“包装”头文件,如果一些讨厌的第三方开发者没有订阅我的愿景:-)
把我自己的砖添加到墙上。
- 每个标头都需要自给自足,只有在首先包含至少一次时才能进行测试
- 不应该通过引入符号(宏,类型等)错误地修改第三方标题的含义
所以我通常会这样:
// myproject/src/example.cpp #include "myproject/example.h" #include #include #include #include <3rdparty/foo.h> #include <3rdparty/bar.h> #include "myproject/another.h" #include "myproject/specific/bla.h" #include "detail/impl.h"
每个组用下一个空白行分隔:
- 首先对应于此cpp文件的标题(健全性检查)
- 系统标头
- 第三方标头,按依赖顺序组织
- 项目标题
- 项目私有标题
另请注意,除了系统标头之外,每个文件都位于具有其命名空间名称的文件夹中,因为它更容易以这种方式跟踪它们。
我非常确定这不是一个在理智的世界中任何地方推荐的做法,但我喜欢行系统包括文件名长度,在相同的长度内排序。 像这样:
#include #include #include #include
我认为在其他人之前包含你自己的标题是个好主意,以避免包含顺序依赖的耻辱。
我建议:
- 您正在构建的.cc模块的标头。 (帮助确保项目中的每个标头都没有对项目中其他标头的隐式依赖关系。)
- C系统文件。
- C ++系统文件。
- 平台/ OS /其他头文件(例如win32,gtk,openGL)。
- 项目中的其他头文件。
当然,在可能的情况下,每个部分的字母顺序。
始终使用前向声明以避免头文件中不必要的#include
。
这不是主观的。 确保您的标题不依赖于按特定顺序排列#include
d。 您可以确定包含STL或Boost标头的顺序无关紧要。
首先包括与.cpp相对应的标题…换句话说, source1.h
在包含任何其他source1.h
之前应该包含source1.h
。 我能想到的唯一例外是当MSVC与预编译头文件一起使用时,在这种情况下,你必须先将stdafx.h
包括在内。
推理:在任何其他文件之前包含source1.h
可以确保它可以独立存在而没有它的依赖性。 如果source1.h
在以后的日期具有依赖性,编译器将立即提醒您将所需的前向声明添加到source1.h
。 这反过来确保标题可以由其依赖者以任何顺序包含在内。
例:
source1.h
class Class1 { Class2 c2; // a dependency which has not been forward declared };
source1.cpp
#include "source1.h" // now compiler will alert you saying that Class2 is undefined // so you can forward declare Class2 within source1.h ...
MSVC用户:我强烈建议使用预编译的头文件。 因此,将标准头文件(以及其他永远不会更改的头文件)的所有#include
指令移动到stdafx.h
。
包括从最具体到最不具体的,从.cpp的相应.hpp开始,如果存在.cpp。 这样,将揭示头文件中任何不自足的隐藏依赖项。
使用预编译的头文件很复杂。 解决此问题的一种方法是,在不使项目编译器特定的情况下,使用其中一个项目头作为预编译头文件包含文件。
在C / C ++世界中,这是一个很难的问题,除了标准之外还有很多元素。
我认为头文件顺序不是一个严重的问题,只要它编译,就像squelart说的那样。
我的想法是:如果所有这些标题中没有符号冲突,任何顺序都可以,并且稍后可以通过将#include行添加到有缺陷的.h来修复标题依赖性问题。
当一些标题根据上面的标题改变其动作(通过检查#if条件)时,会出现真正的麻烦。
例如,在VS2005中的stddef.h中,有:
#ifdef _WIN64 #define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) ) #else #define offsetof(s,m) (size_t)&(((s *)0)->m) #endif
现在的问题是:如果我有一个需要与许多编译器一起使用的自定义头(“custom.h”),包括一些不在其系统头中提供offsetof
旧版本,我应该在我的头文件中写入:
#ifndef offsetof #define offsetof(s,m) (size_t)&(((s *)0)->m) #endif
并确保在所有系统头之后告诉用户#include "custom.h"
,否则,stddef.h中的offsetof
行将断言宏重定义错误。
我们祈祷不要在我们的职业生涯中遇到更多此类案件。