C / C ++包含文件顺序/最佳实践
是否build议在包含文件应指定的顺序?
例如,系统文件,STL,Boost,之前的本地包含文件? 有一个特别的理由select一个或另一个? 我假设包含文件具有指定的适当的依赖关系。
我不认为有一个build议的命令,只要它编译! 令人讨厌的是,当某些标题需要首先包含其他标题时…这是标题本身的问题,而不是包含的顺序。
我个人的偏好是从本地到全球,每个部分按字母顺序排列,即:
- h文件对应的cpp文件(如果适用)
- 来自相同组件的头文件,
- 其他组件的头文件,
- 系统标题。
我的理由是,它应该certificate每个头(有一个CPP)可以#include
d没有先决条件。 而其余的似乎从这里逻辑stream动。
要记住的重点是你的头文件不应该依赖于其他头文件。 确保这一点的一种方法是在任何其他标题之前包含标题。
“C ++思维”特别提到了这个,参考了Lakos的“大规模C ++软件devise”:
潜在的使用错误可以通过确保组件的.h文件自行parsing来避免,而不需要外部提供的声明或定义。将.h文件作为.c文件的第一行包括在内,可以确保没有关键的部分.h文件中缺less组件的物理接口固有的信息(或者,如果有的话,只要尝试编译.c文件,就会发现它)。
也就是说,按以下顺序包括:
- 此实现的原型/接口标头(即对应于此.cpp / .cc文件的.h / .hh文件)。
- 根据需要,来自同一个项目的其他头文件。
- 来自其他非标准,非系统库(例如Qt,Eigen等)的头文件。
- 来自其他“几乎标准”的库(例如Boost)
- 标准的C ++头文件(例如,iostream,function等)
- 标准的C头(例如,cstdint,dirent.h等)
如果任何标题包含在这个顺序中有问题,或者修复它们(如果你的)或者不使用它们。 抵制不写清洁标题的库。
Google的C ++风格指南(http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml)认为;几乎相反,根本没有任何理由; 我个人倾向于赞成Lakos方法。
我遵循两个简单的规则来避免绝大多数的问题:
- 所有的头文件(甚至任何源文件)都应该包含他们需要的东西。 他们不应该依赖包括用户在内的用户。
- 作为一个附件,所有头文件都应该包含守卫,这样他们就不会被上面的规则1的雄心勃勃的应用多次包含在内。
我也遵循以下准则:
- 首先包含系统头文件(stdio.h等)和分界线。
- 按逻辑分组。
换一种说法:
#include <stdio.h> #include <string.h> #include "btree.h" #include "collect_hash.h" #include "collect_arraylist.h" #include "globals.h"
虽然,作为指导,这是一个主观的事情。 另一方面,我严格执行规则,即使是提供包装器头文件,也包括一些令人讨厌的第三方开发人员不认可我的愿景:-)
我敢肯定,这是不是在理智的世界任何地方推荐的做法,但我喜欢线系统包括文件名长度,在同一长度词法sorting。 像这样:
#include <set> #include <vector> #include <algorithm> #include <functional>
我认为把自己的标题包含在其他人之前是一个好主意,以避免包含顺序依赖的羞耻。
把我自己的砖头添加到墙上。
- 每个标题必须是自给自足的,只有至less包含一次才能被testing
- 不应该通过引入符号(macros,types等)来错误地修改第三方标题的含义。
所以我通常是这样的:
// 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"
每个组由下一个空行分隔:
- 首先对应于此cpp文件的标题(完整性检查)
- 系统标题
- 第三方标题,按照依赖性顺序组织
- 项目标题
- 项目专用标题
另外请注意,除了系统标题之外,每个文件都位于一个名称空间名称的文件夹中,只是因为这样更容易跟踪它们。
我build议:
- 您正在构build的.cc模块的标头。 (有助于确保项目中的每个标题对项目中的其他标题没有隐式依赖关系。)
- C系统文件。
- C ++系统文件。
- 平台/操作系统/其他头文件(如win32,gtk,openGL)。
- 其他头文件从您的项目。
当然,如果可能的话,在每个部分内按字母顺序排列。
总是使用前向声明来避免头文件中不必要的#include
。
这不是主观的。 确保你的头文件不依赖#include
d的特定顺序。 您可以确定,包含STL或Boost标头的顺序无关紧要。
首先包含与.cpp相对应的头文件,换句话说, 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用户:我强烈build议使用预编译头文件。 因此,将标准头文件(以及其他永远不会改变的头文件)的所有#include
指令移至stdafx.h
。
包括从最具体到最不具体的,从.cpp对应的.hpp开始,如果存在的话。 这样,头文件中不能自给自足的任何隐藏的依赖关系将被揭示。
由于使用了预编译头文件,这变得复杂了。 解决这个问题的一个方法就是,不用编译器特定的项目,就是使用其中一个项目头文件作为预编译头文件。
在C / C ++世界中,这是一个很难回答的问题,超出标准的元素太多了。
我认为头文件的顺序不是一个严重的问题,只要编译,就像squelart说的那样。
我的想法是:如果所有这些头文件中没有符号冲突,那么任何顺序都是可以的,头文件依赖性问题稍后可以通过向有缺陷的.h添加#include行来解决。
当一些头根据上面的标题改变其行为(通过检查#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
的行会声明macros重定义错误。
我们祈祷在职业生涯中不要再遇到这种情况。