标头/ cpp文件中应声明标头的顺序是什么?显然,后续标题所需的那些应该更早,类特定标题应该在cpp范围内而不是标题范围,但是是否有一个集合顺序约定/最佳实践?
答案 0 :(得分:63)
在头文件中,您必须包含所有标头以使其可编辑。并且不要忘记使用前向声明而不是某些标题。
在源文件中:
按照这个顺序,你不会错过任何忘记自己包含库的头文件。
答案 1 :(得分:21)
良好做法:每个.h文件都应该有一个.cpp,首先包含.hpp。这证明任何.h文件都可以放在第一位。
即使标头不需要实现,也可以创建一个仅包含该.h文件的.cpp,而不是其他内容。
这意味着您可以按照自己喜欢的方式回答问题。你把它们包含在哪个顺序并不重要。
有关更多精彩提示,请尝试使用本书:Large-Scale C++ Software Design - 这太遗憾了,但它实际上是C ++源代码布局的生存指南。
答案 2 :(得分:6)
在头文件中,我倾向于首先放置标准头,然后是我自己的头(两个列表按字母顺序排序)。在实现文件中,我首先将标题对应(如果有的话),然后是标准标题和其他依赖标题。
顺序并不重要,除非您充分利用宏和#define
;在这种情况下,您必须检查您定义的宏是否不替换之前包含的宏(当然,除非您想要的是这样)。
关于本声明
后续标题所需的那些应该更早
标题不应该依赖于之前包含的其他标题!如果它需要标题,它只包括它们。标题保护将阻止多重包含:
#ifndef FOO_HEADER_H
#define FOO_HEADER_H
...
#endif
修改的
由于我写了这个答案,我改变了在代码中对include指令进行排序的方法。现在,我尝试始终按标准化的顺序添加标题,因此我的项目的标题首先出现,然后是第三方库标题,然后是标准标题。
例如,如果我的某个文件使用了我编写的库,Qt,Boost和标准库,我将按以下顺序订购包含:
//foo.cpp
#include "foo.hpp"
#include <my_library.hpp>
// other headers related to my_library
#include <QtCore/qalgorithms.h>
// other Qt headers
#include <boost/format.hpp> // Boost is arguably more standard than Qt
// other boost headers
#include <algorithms>
// other standard algorithms
我这样做的原因是在我自己的标题中检测缺少的依赖项:让我们假设my_library.hpp
使用std::copy
,但不包括<algorithm>
。如果我在<algorithm>
foo.cpp
之后将其包含在内,则会忽略此缺失的依赖关系。相反,根据我刚才提出的顺序,编译器会抱怨std::copy
尚未声明,允许我更正my_library.hpp
。
在每个“库”组中,我尝试按字母顺序排列include指令,以便更容易地找到它们。
在旁注中,一个好的做法是最大限度地限制头文件之间的依赖关系。文件应包含尽可能少的标头,尤其是头文件。实际上,您包含的标头越多,在更改内容时需要重新编译的代码越多。限制这些依赖关系的一个好方法是使用前向声明,这在头文件中通常是足够的(参见When can I use a forward declaration?)。
答案 3 :(得分:5)
Google C++ Style Guide, Names and Order of Includes:
在dir / foo.cc中,其主要目的是在dir2 / foo2.h中实现或测试内容,按如下方式订购包含:
- dir2 / foo2.h(首选地点 - 见下文详情)。
- C系统文件。
- C ++系统文件。
- 其他图书馆的.h文件。
- 您项目的.h文件。
答案 4 :(得分:3)
我以前按字母顺序排序(更容易找到)
答案 5 :(得分:2)
“如何”并不明显,但“是什么”。 您的目标是确保包含头文件的顺序永远不重要(我的意思是“永远不要!”)。
一个很好的帮助是在构建仅包含其中一个的cpp文件(每个头文件一个)时测试头文件是否编译。
答案 6 :(得分:1)
对于.cpp文件,您应该包括该类的标头或您首先要实现的任何内容,因此您将捕获此标头缺少某些包含的情况。在此之后,大多数编码指南往往首先包括系统标题,其次是项目标题,例如Google C++ Style Guide。
答案 7 :(得分:0)
这是依赖性的东西,它在很大程度上取决于你在我们的标题中放置的内容。一个事实是,你可能真的臭名昭着,并尽量减少你的包含严格,但你最终会遇到一个你想要使用包含警卫的场景。
#ifndef MY_HEADER_H
#define MY_HEADER_H
//...
#endif
问题在开始时并不明显,但随着软件复杂性的增加,依赖关系也会增加。你可以做得很好,并且对它很聪明,但是更大的C ++项目通常都包含在内。你可以尝试,但你只能这么做。所以要勤奋并考虑你的包含,是的!但是你肯定会在某些时候有循环依赖,这就是你需要包含守卫的原因。
答案 8 :(得分:0)
如果标题需要其他标题,那么它只是将它们包含在该标题中。
尝试构建代码,以便传递指针或引用,并在可能的位置转发声明。
在实现中,应首先列出定义它的标头(除非在Visual Studio中使用pch,否则stdafx将首先出现)。
我通常会根据需要列出它们。
答案 9 :(得分:-1)
我发现以下约定最有用:
module.cpp:
// this is the header used to trigger inclusion of precompiled headers
#include <precompiled.h>
// this ensures that anything that includes "module.h" works
#include "module.h"
// other headers, usually system headers, the project
重要的是将模块的标头作为第一个非预编译的标头。这可以确保“module.h”没有意外的依赖关系。
如果您正在处理磁盘访问时间较慢的大型项目,我已经看到这种样式用于减少构建时间:
module.cpp:
// this is the header used to trigger inclusion of precompiled headers
#include <precompiled.h>
// this ensures that anything that includes "module.h" works
#include "module.h"
// other headers, usually system headers, the project
#if !defined _OTHER_MODULE_GUARD_
#include "other_module.h"
#endif
#if !defined _ANOTHER_MODULE_GUARD_
#include "another_module.h"
#endif
它有点冗长,但确实保存在磁盘上寻找,因为如果已经包含了标题,则不会搜索/打开标题。如果没有防护检查,编译器将寻找并打开头文件,解析整个文件以结束#ifdef
整个文件。