在头文件和源文件中包含#includes

时间:2010-04-07 22:51:12

标签: c++ file header include

我喜欢将所有#includes放在我的头文件中,然后只在我的源文件中包含我的源文件头。什么是行业标准?我的方法有没有退缩?

1 个答案:

答案 0 :(得分:63)

通常,您只想将最少的必要包含放入类头文件中,因为使用该标头的任何其他人也将被强制为#include所有这些。在较大的项目中,这会导致构建速度降低,依赖性问题以及各种其他肮脏问题。

将头文件视为您的类的公共接口。除非他们必要才能使用该类,否则您不想让使用它的每个人都有额外的依赖关系。

将类实现中仅需要的任何内容移动到源文件中。对于标题中使用的其他类,只有#include标题,如果您确实需要知道标题中的标题 - 其他任何内容和forward declaration就足够了。大多数情况下,您只需要继承的#include个类,以及其对象是您的类的值成员的类。

This page有一个很好的总结。 (以下复制以供参考)


C ++头文件包含模式#

即使在C语言编程中,大型软件项目也需要仔细的头文件管理。当开发人员转向C ++时,头文件管理变得更加复杂和耗时。在这里,我们提供了一些头文件包含模式,这将简化这项工作。

头文件包含规则

在这里,我们讨论了简化头文件管理所需的C ++头文件包含的基本规则。

只有在前向声明不能完成工作时才应包含头文件。 头文件的设计应使头文件包含的顺序不重要。 这是通过确保x.hx.cpp中的第一个头文件来实现的。 头文件包含机制应该容忍重复头文件包含。 以下部分将借助示例解释这些规则。

头文件包含示例

以下示例说明了不同类型的依赖项。假设A类的代码存储在a.cppa.h

a.h

#ifndef _a_h_included_
#define _a_h_included_
#include "abase.h"
#include "b.h"

// Forward Declarations
class C;
class D;

class A : public ABase
{
  B m_b;
  C *m_c;
  D *m_d;

public:
  void SetC(C *c);
  C *GetC() const;

  void ModifyD(D *d);
};
#endif

a.cpp

#include "a.h"
#include "d.h"

void A::SetC(C* c)
{
  m_c = c;
}

C* A::GetC() const
{
  return m_c;
}

void A::ModifyD(D* d)
{
  d->SetX(0);
  d->SetY(0);
  m_d = d;
}

文件包含分析

让我们从本示例中涉及的类的角度分析头文件包含,即ABaseABC和{{1 }}

  • 类ABase: D是基类,因此需要类声明才能完成类声明。编译器需要知道ABase的大小以确定ABase的总大小。在这种情况下,A中应明确包含abase.h
  • B类:a.h按值包含类A,因此需要类声明才能完成类声明。编译器需要知道B的大小以确定B的总大小。在这种情况下,A中应明确包含b.h
  • C类a.h仅作为指针引用包含在内。 Class C的大小或实际内容对Ca.h并不重要。因此a.cpp中只包含一个前瞻性声明。请注意,a.hc.h中未包含a.h
  • D类:类a.cpp仅用作D中的指针引用。因此,前瞻性声明就足够了。但a.h实质上使用了类a.cpp,因此明确包含D

要点

只有在前向声明无法完成工作时才应包含头文件。如果不包括d.hc.h其他类d.h的客户,则永远不必担心Ac.h,除非他们按值使用C类和D类。 d.h中已包含a.h作为第一个头文件。这将确保a.cpp不期望在a.h之前包含某些头文件。由于a.h已作为第一个文件包含在内,因此a.h的成功编译将确保a.cpp不期望在a.h之前包含任何其他头文件。 如果所有类都遵循此类(即a.h始终包含x.cpp作为第一个标头),则不会依赖于头文件包含。 x.h包括检查符号a.h的预处理器定义。这使得它可以容忍重复_a_h_included_的包含。

循环依赖

以下示例中,类a.hX之间存在循环依赖关系。这种依赖关系是通过使用前向声明来处理的。

Y

x.h and y.h