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

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

【问题讨论】:

    标签: c++ header include code-organization


    【解决方案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 整个文件。

          【讨论】:

            猜你喜欢
            • 2021-11-02
            • 1970-01-01
            • 2017-06-19
            • 1970-01-01
            • 2011-07-11
            • 1970-01-01
            • 2016-12-17
            • 2018-07-08
            • 1970-01-01
            相关资源
            最近更新 更多