应该以什么顺序指定包含文件,即包含一个头文件在另一个头文件之前的原因是什么?
例如,系统文件、STL和Boost是在本地包含文件之前还是之后?
这不是主观的。确保你的头文件不依赖于#included的特定顺序。可以肯定的是,不管你以什么顺序包含STL或Boost头文件。
#include
我不认为有一个推荐的顺序,只要它编译!令人恼火的是,有些头文件需要先包含其他头文件……这是头文件本身的问题,而不是包含顺序的问题。
我个人倾向于从局部到全局,每个小节按字母顺序排列,即:
我的理由是1。它应该证明每个头(有cpp)可以在没有先决条件的情况下#included (terminus technicus:头是“自包含的”)。剩下的似乎就从这里顺理成章地展开了。
我很确定这在任何理智的世界里都不是一个推荐的实践,但我喜欢按文件名长度排列系统包含,在相同的长度内按词法排序。像这样:
#include <set> #include <vector> #include <algorithm> #include <functional>
我认为在其他人之前包含自己的头文件是一个好主意,以避免包含顺序依赖的耻辱。
我遵循两个简单的规则来避免绝大多数问题:
我还遵循以下准则:
换句话说:
#include <stdio.h> #include <string.h> #include "btree.h" #include "collect_hash.h" #include "collect_arraylist.h" #include "globals.h"
虽然,作为指导方针,这是主观的事情。另一方面,我严格执行规则,甚至提供'包装'头文件包含保护和分组包含,如果一些讨厌的第三方开发人员不同意我的愿景:-)
我建议:
当然,在可能的情况下,每个部分都要按字母顺序排列。
总是使用前向声明来避免头文件中不必要的__abc。
首先包含与.cpp…换句话说,source1.cpp应该在包含任何其他内容之前包含source1.h。我能想到的唯一例外是在使用带有预编译头文件的MSVC时,在这种情况下,你被迫在其他任何东西之前包含stdafx.h。
source1.cpp
source1.h
stdafx.h
在任何其他文件之前包含source1.h可以确保它可以独立于它的依赖项。如果source1.h在以后有了依赖项,编译器会立即提醒你向source1.h添加所需的前向声明。这反过来又确保了头文件可以由它们的依赖项以任何顺序包含。
例子:
class Class1 { Class2 c2; // a dependency which has not been forward declared };
#include "source1.h" // now compiler will alert you saying that Class2 is undefined // so you can forward declare Class2 within source1.h ...
我强烈建议使用预编译头文件。因此,将标准标头(以及其他永远不会改变的标头)的所有#include指令移动到stdafx.h。
从最特定到最不特定,如果存在.cpp,则从对应的.hpp开始。这样,头文件中任何不是自给自足的隐藏依赖关系都将被显示出来。
由于使用了预编译的头文件,这变得更加复杂。解决这个问题的一种方法是,在不特定于项目编译器的情况下,使用一个项目头作为预编译的头包含文件。
给自己添砖加瓦。
所以我通常是这样的:
// 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"
每组之间用空行分隔:
还要注意,除了系统头文件外,每个文件都位于一个与其名称空间同名的文件夹中,因为这样更容易跟踪它们。
在C/ c++世界中,这是一个很难回答的问题,因为有太多超出标准的元素。
我认为头文件的顺序不是一个严重的问题,只要它编译,就像squelart说的。
我的想法是:如果所有这些标题中的符号没有冲突,任何顺序都是OK的,并且标题依赖问题可以在以后通过在有缺陷的.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的旧编译器,我应该在头文件中写道:
offsetof
#ifndef offsetof #define offsetof(s,m) (size_t)&(((s *)0)->m) #endif
并且一定要告诉用户#include "custom.h" 后所有系统头文件,否则,stddef.h中的offsetof行将断言一个宏重定义错误。
#include "custom.h"
我们祈祷在我们的职业生涯中不再遇到这种情况。
要记住的一件重要的事情是,您的头文件不应该依赖于首先包含的其他头文件。确保这一点的一种方法是在任何其他头文件之前包含您的头文件。
《c++思维》特别提到了这一点,引用了Lakos的《大规模c++软件设计》:
可以通过确保组件的.h文件自己进行解析来避免潜在的使用错误——没有外部提供的声明或定义……将.h文件作为.c文件的第一行,可以确保.h文件中不缺少组件物理接口固有的关键信息(或者,如果有的话,在尝试编译.c文件时就会发现它)。
也就是说,按以下顺序包括:
如果任何标题在这个顺序中有问题,要么修复它们(如果你的),要么就不要使用它们。抵制不编写干净头文件的库。
谷歌的c++风格指南认为几乎是相反的,实际上没有任何理由;我个人倾向于Lakos方法。
在决定特定的包含顺序时,需要合并几个单独的考虑事项。让我试着理清头绪。
许多回答认为,include顺序应该作为头文件是否自包含的检查。这就混淆了测试和编译的考虑
您可以单独检查您的头文件是否自包含。“静态分析”;独立于任何编译过程。例如,运行
gcc headerfile.h -fsyntax-only
测试头文件是否自包含可以很容易地编写脚本/自动化。甚至你的makefile也可以这样做。
无意冒犯,但Lakos的书是1996年的,把这些不同的问题放在一起听起来像90年代风格的编程。也就是说,有些生态系统(现在的Windows还是90年代的Windows ?)缺乏脚本/自动化测试的工具。
另一个考虑因素是可读性。当您查找源文件时,您只想轻松地查看包含了哪些内容。因为你的个人品味和喜好是最重要的,尽管通常情况下你要么按照最具体到最不具体的顺序排列,要么相反(我更喜欢后者)。
在每个组中,我通常只是按字母顺序排列。
如果头文件是自包含的,那么编译结果的包含顺序严格来说是根本不重要。
也就是说,除非你对你的代码有(可疑的?)特定的设计选择,比如必要的宏定义不会自动包含在内。在这种情况下,您应该重新考虑您的程序设计,尽管它当然可能非常适合您。