C ++是否有标准的#include约定?

时间:2009-03-27 19:03:14

标签: c++ include

这是一个相当基本的问题,但它是一个让我误解了一段时间的问题。

我的项目有一堆.cpp(实现)和.hpp(定义)文件。

我发现当我添加其他类和更多类的相互依赖时,我必须#include其他头文件。一两个星期之后,我最终在很多地方使用了#include指令。稍后,我会尝试删除一些#includes并发现一切仍然有效,因为其他包含的类也包括#include我刚删除的内容。

是否有一个简单易行的规则可以放入#includes来阻止这个丑陋的混乱发生在一开始?什么是最佳做法?

例如,我曾参与过一些项目,其中Implementation .cpp文件只包含相应的Definition .hpp文件,而没有别的。如果Implementation .cpp需要使用任何其他.hpp文件,则它们都由Definition .hpp文件引用。

8 个答案:

答案 0 :(得分:18)

一些最佳做法:

  • 每个.cpp或.C文件都包含所需的所有标头,不依赖于包含其他相关标头的标头
  • 每个.hpp或.h文件都包含其所有依赖项,并且不依赖于包含其他相关标头的包含标头
  • 每个标题都包含:

    #ifndef HEADER_XXX_INCLUDED
    #define HEADER_XXX_INCLUDED
    ...
    #endif /* HEADER_XXX_INCLUDED */
    
  • 标题不会在周期中包含彼此

  • 通常:有一个“项目范围的头文件”,如“config.h”或“.h”,它总是被任何.cpp或.C文件包含在。通常,这具有与平台相关的配置数据,项目范围的常量和宏等。

这些不一定是“最佳实践”,但我通常遵循的规则也是:

  • 项目特定标头包含在#include "..."之前和系统范围标头之前,其中包含#include <...>
  • 项目特定的标题包含在按字母顺序排列顺序中,作为确保不包含它们的顺序的意外隐藏要求的方法。由于每个标题应包含其依赖项,因此标题应受到保护以防止多次包含,您应该能够按照您希望的任何顺序包含它们。

答案 1 :(得分:10)

查看John Lakos的大规模C ++软件设计。这就是我所遵循的(作为例子写的):

接口

// foo.h
// 1) standard include guards.  DO NOT prefix with underscores.
#ifndef PROJECT_FOO_H
#define PROJECT_FOO_H

// 2) include all dependencies necessary for compilation
#include <vector>

// 3) prefer forward declaration to #include
class Bar;
class Baz;
#include <iosfwd> // this STL way to forward-declare istream, ostream

class Foo { ... };
#endif

实施

// foo.cxx
// 1) precompiled header, if your build environment supports it
#include "stdafx.h"

// 2) always include your own header file first
#include "foo.h"

// 3) include other project-local dependencies
#include "bar.h"
#include "baz.h"

// 4) include third-party dependencies
#include <mysql.h>
#include <dlfcn.h>
#include <boost/lexical_cast.hpp>
#include <iostream>

预编译标题

// stdafx.h
// 1) make this easy to disable, for testing
#ifdef USE_PCH

// 2) include all third-party dendencies.  Do not reference any project-local headers.
#include <mysql.h>
#include <dlfcn.h>
#include <boost/lexical_cast.hpp>
#include <iosfwd>
#include <iostream>
#include <vector>
#endif

答案 2 :(得分:7)

我总是使用最小耦合原理。如果当前文件实际需要,我只包含一个文件;如果我可以使用前向声明而不是完整定义,我将使用它。我的.cpp文件总是在顶部有一堆#includes。

Bar.h:

class Foo;

class Bar
{
    Foo * m_foo;
};

Bar.cpp:

#include "Foo.h"
#include "Bar.h"

答案 3 :(得分:5)

仅使用所需的最少量包含。无用包括减慢编译速度。

此外,如果只需要指向类,则不必包含标题。在这种情况下,您可以使用前向声明,如:

class BogoFactory;

编辑:只是为了说清楚。当我说最低金额时,我并不是指建立包含链条的链接:

a.h
#include "b.h"

b.h
#include "c.h"

如果a.h需要c.h,当然需要将其包含在a.h中以防止出现维护问题。

答案 4 :(得分:3)

C / C ++中使用的#include模型存在一些问题,主要的一个问题是它没有表达实际的依赖图。相反,它只是按照特定的顺序连接一堆定义,通常导致每个源文件中的定义以不同的顺序排列。

通常,您需要知道软件的包含文件层次结构,就像您了解数据结构一样;你必须知道哪些文件包含在哪里。阅读您的源代码,了解哪些文件在层次结构中处于高位,这样您就可以避免意外添加包含,以便“从任何地方”包含它。添加新的包含时请认真思考:我是否真的需要在此处添加此内容? 我执行此操作时会提取哪些其他文件?

两个惯例(除了已经提到的那些)可以提供帮助:

  • 一个类==一个源文件+一个头文件,一致命名。 A类进入A.cpp和A.h.代码模板和代码片段很适合减少在单独文件中声明每个类所需的输入量。
  • 使用Impl-pattern避免在头文件中暴露内部成员。 impl模式意味着将所有内部成员放在.cpp文件中定义的结构中,并且在类中只有一个带有前向声明的私有指针。这意味着头文件只需要包含其公共接口所需的那些头文件,并且其内部成员所需的任何定义都将保留在头文件之外。

答案 5 :(得分:2)

以antti.huima所说的为基础:

假设你有A,B和C类.A取决于(包括)B,A和B都依赖于C.有一天你发现你不再需要在A中包含C,因为B做到了为你,所以你删除了#include声明。

现在如果将来更新B不再使用C,会发生什么?突然间,A没有任何理由被打破。

答案 6 :(得分:1)

在A.cpp中,首先要包括A.h,以确保A.h没有其他依赖项。 在所有系统文件之前包括所有项目文件之前的所有本地(相同模块)文件,再次确保不依赖于预先包含的系统文件。 尽可能使用前向声明。 使用#indef / #define / #endif pattern 如果A.h中包含标题,则无需将其包含在A.cpp中。必须明确包含A.cpp所需的任何其他标头,即使它们恰好由其他.h文件提供。

答案 7 :(得分:0)