web-dev-qa-db-ja.com

ヘッダーファイルとソースファイルに#includesを含める

私はすべての#includeをヘッダーファイルに入れてから、そのソースファイルのヘッダーのみをソースファイルに含めるようにします。業界標準とは何ですか?メソッドに欠点はありますか?

49
Rob from Utah

一般に、必要な最小限のインクルードのみをクラスヘッダーファイルに配置する必要があります。そのヘッダーを使用する他のユーザーは、それらもすべて#includeに強制されます。大規模なプロジェクトでは、これによりビルドが遅くなり、依存関係の問題が発生し、その他のあらゆる厄介な問題が発生します。

ヘッダーファイルは、クラスへのパブリックインターフェイスと考えてください。クラスを使用できるようにするためにnecessaryでない限り、それを使用するすべての人を余分な依存関係でsしたくありません。

クラス実装でのみ必要なものはすべてソースファイルに移動します。ヘッダーで使用される他のクラスの場合、ヘッダーのサイズまたは内容を実際に知る必要がある場合は、ヘッダーのみ#includeを使用します-その他および 前方宣言 で十分です。ほとんどの場合、#include継承元のクラス、およびオブジェクトがクラスの値メンバーであるクラスのみが必要です。

このページ には要約があります。 (参照用に以下に複製)


C++ヘッダーファイルのインクルードパターン#

大規模なソフトウェアプロジェクトでは、Cでプログラミングする場合でも慎重なヘッダーファイル管理が必要です。開発者がC++に移行すると、ヘッダーファイル管理はさらに複雑で時間がかかります。ここでは、この雑用を簡素化するヘッダーファイルの包含パターンをいくつか示します。

ヘッダーファイルの包含ルール

ここでは、ヘッダーファイルの管理を簡素化するために必要なC++ヘッダーファイルを含める基本的なルールについて説明します。

ヘッダーファイルは、前方宣言がジョブを実行しない場合にのみ含める必要があります。ヘッダーファイルは、ヘッダーファイルを含める順序が重要ではないように設計する必要があります。これは、x.hx.cppの最初のヘッダーファイルであることを確認することで実現されます。ヘッダーファイルのインクルードメカニズムは、ヘッダーファイルのインクルードの複製を許容する必要があります。次のセクションでは、例を使用してこれらの規則について説明します。

ヘッダーファイルのインクルードの例

次の例は、さまざまなタイプの依存関係を示しています。 a.cppおよびa.hにコードが格納されているクラスAを想定します。

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、およびD

  • クラスABase:ABaseは基本クラスであるため、クラス宣言を完了するにはクラス宣言が必要です。コンパイラは、ABaseの合計サイズを決定するために、Aのサイズを知る必要があります。この場合、abase.ha.hに明示的に含める必要があります。
  • クラスB:クラスAには値によるクラスBが含まれているため、クラス宣言を完了するにはクラス宣言が必要です。コンパイラは、Aの合計サイズを決定するためにBのサイズを知る必要があります。この場合、b.ha.hに明示的に含める必要があります。
  • クラスCClass Cは、ポインター参照としてのみ含まれています。 Cのサイズまたは実際のコンテンツは、a.hまたはa.cppにとって重要ではありません。したがって、a.hには前方宣言のみが含まれています。 c.ha.hまたはa.cppのいずれにも含まれていません。
  • クラスD:クラスDは、a.hのポインター参照としてのみ使用されます。したがって、前方宣言で十分です。ただし、a.cppは実質的にクラスDを使用するため、d.hを明示的に含めます。

キーポイント

ヘッダーファイルは、前方宣言がジョブを実行しない場合にのみ含める必要があります。 c.hd.hを含めないことにより、クラスAの他のクライアントは、値でクラスCとDを使用しない限り、c.hd.hを心配する必要はありません。 a.hは、a.cppの最初のヘッダーファイルとして含まれています。これにより、a.hは、a.hの前に特定のヘッダーファイルが含まれることを期待しません。 a.hが最初のファイルとして含まれているので、a.cppのコンパイルが成功すると、a.ha.hの前に他のヘッダーファイルが含まれることはありません。すべてのクラスでこれに従う場合(つまり、x.cppには常に最初のヘッダーとしてx.hが含まれます)、ヘッダーファイルのインクルードに依存しません。 a.hには、シンボル_a_h_included_のプリプロセッサ定義のチェックが含まれます。これにより、a.hの包含の複製が許容されます。

循環依存

次の例では、クラスXYの間に循環依存関係が存在します。この依存関係は、前方宣言を使用して処理されます。

x.h and y.h

/* ====== x.h ====== */
// Forward declaration of Y for cyclic dependency
class Y;

class X 
{
    Y *m_y;
    ...
};

/* ====== y.h ====== */
// Forward declaration of X for cyclic dependency
class X;

class Y 
{
    X *m_x;
    ...
};
65
tzaman