C ++标头顺序[关闭]

Posted

技术标签:

【中文标题】C ++标头顺序[关闭]【英文标题】:C++ Header order [closed] 【发布时间】:2010-10-11 11:42:28 【问题描述】:

header/cpp 文件中的 headers 应该以什么顺序声明?显然,后续标头所需的那些应该更早,并且特定于类的标头应该在 cpp 范围内而不是标头范围内,但是是否有固定的顺序约定/最佳实践?

【问题讨论】:

【参考方案1】:

在头文件中,您必须包含所有头文件才能使其可编译。并且不要忘记使用前向声明而不是某些标题。

在源文件中:

对应的头文件 必要的项目标题 第三方库标头 标准库头文件 系统头文件

按此顺序,您将不会错过任何忘记自己包含库的头文件。

【讨论】:

我认为“必要的项目标头”应该在两个项目上分开: - 来自同一个库的项目标头; - 来自其他项目库的头文件(按照最常见项目库头文件的顺序,而不是一些特殊项目库头文件) 我真的很喜欢您的订购。这样,标头就不会依赖其他标头来包含它们的依赖项。 并在每个组中按字母顺序排列,以防止重复包含语句。 预编译的头文件应该是第一个。【参考方案2】:

良好做法:每个 .h 文件都应该有一个 .cpp,它首先包含该 .h,然后再包含其他任何内容。这证明任何.h文件都可以放在首位。

即使标头不需要实现,您也可以创建一个仅包含该 .h 文件而不包含其他任何内容的 .cpp。

这意味着您可以以任何您喜欢的方式回答您的问题。将它们包含在什么顺序中并不重要。

如需更多精彩提示,请阅读这本书:Large-Scale C++ Software Design - 可惜它太贵了,但它实际上是 C++ 源代码布局的生存指南。

【讨论】:

我在单元测试中这样做。 更容易自动化测试:只需编译 (-c) 每个头文件(是的,g++ -c blah.h)并确保它可以编译。然后,您可以将目标文件扔掉。 +1,该答案中还有很多未说明的地方。很棒的提示! 你也可以用 VC++ 做到这一点:cl.exe /EHsc /TP /c blah.h @VadimFerderer 不幸的是,这不适用于作为具有许多附加路径(包括、库等)的复杂项目的一部分的标头。有什么简单的方法可以让 cl.exe 了解这些路径(它们是在项目文件中设置的)。【参考方案3】:

在头文件中,我倾向于首先放置标准头,然后是我自己的头(两个列表都按字母顺序排列)。在实现文件中,我先放对应的头文件(如果有的话),然后是标准头文件和其他依赖头文件。

顺序并不重要,除非你充分利用宏和#define;在这种情况下,您必须检查您定义的宏是否不会替换之前包含的宏(当然,除非您想要这样做)。

关于此声明

后续标题需要的应该更早

标题不应该依赖于它之前包含的其他标题!如果它需要标题,它只包含它们。标头保护将防止多重包含:

#ifndef FOO_HEADER_H
#define FOO_HEADER_H
...
#endif

编辑

自从我写了这个答案后,我改变了在我的代码中排序包含指令的方式。现在,我尝试始终将标头按标准化的递增顺序排列,因此我项目的标头排在第一位,然后是 3rd 方库标头,然后是标准标头。

例如,如果我的一个文件使用了我编写的库、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,但不包括&lt;algorithm&gt;。如果我在foo.cpp 中的&lt;algorithm&gt; 之后包含它,那么这种缺失的依赖关系将不会被注意到。相反,按照我刚才给出的顺序,编译器会报错std::copy没有被声明,让我更正my_library.hpp

在每个“库”组中,我尽量保持包含指令按字母顺序排列,以便更轻松地找到它们。

顺便说一句,一个好的做法是最大程度地限制头文件之间的依赖关系。文件应包含尽可能少的标头,尤其是标头文件。事实上,你包含的头文件越多,当某些事情发生变化时,需要重新编译的代码就越多。限制这些依赖关系的一个好方法是使用前向声明,这在头文件中通常就足够了(参见When can I use a forward declaration?)。

【讨论】:

+1 用于包含/标题保护。【参考方案4】:

Google C++ Style Guide, Names and Order of Includes:

在 dir/foo.cc 中,其主要目的是实现或测试 dir2/foo2.h 中的东西,按如下顺序排列您的包含:

dir2/foo2.h(首选位置 - 详情见下文)。 C 系统文件。 C++ 系统文件。 其他库的 .h 文件。 您项目的 .h 文件。

【讨论】:

是的,但是 ggl 风格指南中有一些讨厌的东西 :)【参考方案5】:

我以前按字母顺序排列它们(更容易找到)

【讨论】:

这也是 Epic 的做法,在他们的虚幻引擎 4 风格文档中。【参考方案6】:

“如何”并不明显,但“是什么”是显而易见的。 您的目标是确保包含头文件的顺序不重要(我的意思是“从不!”)。

一个很好的帮助是在构建只包含其中一个的 cpp 文件(每个头文件一个)时测试头文件是否编译。

【讨论】:

【参考方案7】:

对于 .cpp 文件,您应该包含类的标头或您首先要实现的任何内容,这样您就可以捕捉到该标头缺少某些包含的情况。之后,大多数编码指南倾向于首先包含系统标头,然后是项目标头,例如Google C++ Style Guide。

【讨论】:

【参考方案8】:

这是一个依赖关系,很大程度上取决于您在我们的标头中放入的内容。事实是,您可能对此非常臭名昭著并尽量减少以保持您的包含严格,但您最终会遇到需要使用包含保护的情况。

#ifndef MY_HEADER_H
#define MY_HEADER_H
//...
#endif

问题在开始时并不那么明显,但随着软件复杂性的增加,您的依赖项也随之增加。您可以做得很好,并且对此很聪明,但是较大的 C++ 项目通常充满了包含。你可以尝试,但你只能做这么多。所以要勤奋,想想你的包括,是的!但是你肯定会在某些时候有循环依赖,这就是你需要包含保护的原因。

【讨论】:

这和调用 '#pragma once' 不一样吗? 是的(或多或少)相同,但 #pragma once 不是标准的。 据记录,#pragma once 是 Microsoft 扩展。如果我正在处理一个多平台项目,我会使用它以及包含保护,因为 #pragma once 实际上会阻止解析文件。 #pragma once 在大多数 C++ 编译器(包括 G++)中都能正常工作。【参考方案9】:

如果一个标头需要其他标头,则只需将它们包含在该标头中即可。

尝试构造您的代码,以便您传递指针或引用并在可能的地方转发声明。

在实现中,定义它的标头应该首先列出(在 Visual Studio 中,如果您使用的是 pch,则 stdafx 将首先列出)。

我通常会根据需要列出它们。

【讨论】:

【参考方案10】:

我发现以下约定最有用:

模块.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”没有意外的依赖关系。

如果您正在处理一个磁盘访问时间很慢的大型项目,我已经看到这种风格用于减少构建时间:

模块.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 

这有点冗长,但确实节省了磁盘查找,因为如果已包含标题,则不会搜索/打开标题。如果没有保护检查,编译器将寻找并打开头文件,解析整个文件以结束 #ifdefing 整个文件。

【讨论】:

以上是关于C ++标头顺序[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Linux 上用 C/C++ 编写 Web 服务器 [关闭]

VSCode运行c ++代码时没有这样的文件或目录[关闭]

Nginx后缺少响应标头[关闭]

C++ Ubuntu。使用 FFMPEG 库编译的多个未定义引用 [关闭]

底层连接被关闭:连接被意外关闭

获取请求未通过授权标头[关闭]