应该指定哪些顺序包含文件,即将一个标题包含在另一个标题之前的原因是什么?
例如,系统文件,STL和Boost是在本地包含文件之前还是之后进行的?
答案 0 :(得分:251)
我不认为有推荐的订单,只要它编译!令人烦恼的是,当某些标题需要首先包含其他标题时...这是标题本身的问题,而不是包含的顺序。
我个人的偏好是从本地到全球,每个小节按字母顺序排列,即:
我的理由是,它应该证明每个标题(有一个cpp)可以是#include
d而没有先决条件。其余的似乎从那里开始逻辑流动。
答案 1 :(得分:86)
要记住的重要一点是,您的标头不应该依赖于首先包含的其他标头。确保这一点的一种方法是在任何其他标题之前包含您的标题。
“用C ++思考”特别提到了这一点,引用了Lakos的“大规模C ++软件设计”:
通过确保组件的.h文件自行解析,可以避免潜在的使用错误 - 没有外部提供的声明或定义......包含.h文件作为.c文件的第一行确保.h文件中没有缺少组件物理接口固有的关键信息(或者,如果有的话,只要您尝试编译.c文件就会发现它。)
也就是说,按以下顺序包括:
如果任何标题包含在此订单中的问题,请修复它们(如果您的)或不使用它们。阻止不编写干净标题的库。
Google's C++ style guide认为几乎反过来,根本没有任何理由;我个人倾向于赞成Lakos方法。
答案 2 :(得分:47)
我遵循两条避免绝大多数问题的简单规则:
我也遵循以下准则:
换句话说:
#include <stdio.h>
#include <string.h>
#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
虽然是准则,但这是一个主观的事情。另一方面,我严格执行规则,甚至提供包含警卫的“包装”头文件,并且如果一些讨厌的第三方开发者没有订阅我的愿景,则分组包括: - )
答案 3 :(得分:19)
将我自己的砖添加到墙上。
所以我通常会这样:
// 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"
每个组用下一个空白行分隔:
另请注意,除了系统标头之外,每个文件都位于一个文件夹中,其名称为其命名空间,因为它更容易以这种方式跟踪它们。
答案 4 :(得分:14)
我很确定这在理智的世界中的任何地方都不是推荐的做法,但我喜欢将系统包括在文件名长度中,在相同长度内按字母顺序排序。像这样:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
我认为在其他人之前包含你自己的标题是个好主意,以避免包含顺序依赖的耻辱。
答案 5 :(得分:13)
我建议:
当然,在可能的情况下,每个部分的字母顺序。
始终使用前向声明来避免头文件中不必要的#include
。
答案 6 :(得分:6)
这不是主观的。确保您的标题不依赖于#include
d按特定顺序排列。您可以确定包含STL或Boost标头的顺序无关紧要。
答案 7 :(得分:3)
首先包含与.cpp相对应的标题...换句话说,source1.cpp
在包含任何其他内容之前应包含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
。
答案 8 :(得分:2)
包括从最具体到最不具体的,从.cpp的相应.hpp开始,如果存在.cpp。这样,将显示头文件中任何不自足的隐藏依赖项。
使用预编译的头文件很复杂。解决此问题的一种方法是,在不使项目编译器特定的情况下,使用其中一个项目头作为预编译头文件包含文件。
答案 9 :(得分:1)
在决定特定的包含顺序时,几个单独的考虑因素被混为一谈。让我试着解开。
许多答案表明包含顺序应该作为检查您的标头是否自包含的检查。这混淆了测试和编译
的考虑您可以单独检查您的标题是否是自包含的。 “静态分析”独立于任何编译过程。例如,运行
gcc headerfile.h -fsyntax-only
测试您的头文件是否自包含可以很容易地编写/自动化。甚至您的 makefile 也可以做到这一点。
无意冒犯,但 Lakos 的书是 1996 年的,将这些不同的关注点放在一起对我来说听起来像是 90 年代风格的编程。话虽如此,有些生态系统(现在的 Windows 还是 90 年代的?)缺乏用于脚本化/自动化测试的工具。
另一个考虑因素是可读性。当您查找源文件时,您只想轻松查看包含的内容。为此,您的个人品味和偏好最重要,尽管通常您要么将它们从最具体到最不具体排序,要么反过来(我更喜欢后者)。
在每个组中,我通常只按字母顺序包含它们。
如果您的头文件是自包含的,那么从技术上讲,包含顺序对于编译结果根本不重要。
也就是说,除非您对代码有(有问题?)特定的设计选择,例如不会自动包含的必要宏定义。在这种情况下,您应该重新考虑您的程序设计,尽管它当然可能非常适合您。
答案 10 :(得分:0)
在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
行将声明宏重新定义错误。< / p>