web-dev-qa-db-ja.com

.hファイルには何が必要ですか?

コードを複数のファイルに分割する場合、正確に何が.hファイルに、何が.cppファイルに入力されるべきでしょうか?

88

ヘッダーファイル(.h)は、複数のファイルで必要になる情報を提供するように設計されています。通常、クラス宣言、関数プロトタイプ、列挙などはヘッダーファイルに格納されます。一言で言えば、「定義」。

コードファイル(.cpp)は、1つのファイルでのみ知る必要がある実装情報を提供するように設計されています。一般的に、関数本体、および他のモジュールがアクセスすべき/決してアクセスしない内部変数は、.cppファイル。一言で言えば、「実装」。

どこに属しているかを判断するための最も簡単な質問は、「これを変更した場合、他のファイルのコードを変更して再度コンパイルする必要がありますか?」です。答えが「はい」の場合、おそらくヘッダーファイルに属します。答えが「いいえ」の場合、おそらくコードファイルに属します。

103
Amber

事実は、C++では、これはCヘッダー/ソース構成よりもやや複雑です。

コンパイラは何を見ますか?

コンパイラは、ヘッダーが適切に含まれている1つの大きなソース(.cpp)ファイルを確認します。ソースファイルは、オブジェクトファイルにコンパイルされるコンパイル単位です。

では、なぜヘッダーが必要なのですか?

あるコンパイル単位には、別のコンパイル単位の実装に関する情報が必要になる可能性があるためです。したがって、たとえば、あるソースで関数の実装を記述し、それを使用する必要がある別のソースでこの関数の宣言を記述することができます。

この場合、同じ情報のコピーが2つあります。どちらが悪か...

解決策は、いくつかの詳細を共有することです。実装はソースに残る必要がありますが、関数などの共有シンボルの宣言、または構造、クラス、列挙などの定義を共有する必要があります。

ヘッダーは、これらの共有された詳細を配置するために使用されます。

複数のソース間で共有する必要があるものの宣言をヘッダーに移動します

これ以上何もない?

C++には、共有する必要があるため、ヘッダーに追加できるものがいくつかあります。

  • インラインコード
  • テンプレート
  • 定数(通常、スイッチ内で使用するもの...)

共有実装を含め、共有する必要があるものすべてにヘッダーを移動します

ヘッダー内にソースが存在する可能性があるということですか?

はい。実際、「ヘッダー」内(つまり、ソース間で共有される)になる可能性のあるものはたくさんあります。

  • 前方宣言
  • 関数/構造体/クラス/テンプレートの宣言/定義
  • インラインおよびテンプレートコードの実装

複雑になり、場合によっては(シンボル間の循環依存関係)、1つのヘッダーに収めることができなくなります。

ヘッダーは3つの部分に分けることができます

これは、極端な場合、次のことができることを意味します。

  • 前方宣言ヘッダー
  • 宣言/定義ヘッダー
  • 実装ヘッダー
  • 実装ソース

テンプレート化されたMyObjectがあるとします。私たちは持つことができました:

// - - - - MyObject_forward.hpp - - - - 
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;

// - - - - MyObject_declaration.hpp - - - - 
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>

template<typename T>
class MyObject
{
   public :
      MyObject() ;
   // Etc.
} ;

void doSomething() ;

// - - - - MyObject_implementation.hpp - - - - 
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>

template<typename T>
MyObject<T>::MyObject()
{
   doSomething() ;
}

// etc.

// - - - - MyObject_source.cpp - - - - 
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>

void doSomething()
{
   // etc.
} ;

// etc.

うわー!

「実生活」では、通常はそれほど複雑ではありません。ほとんどのコードには、ソース内にインラインコードがいくつかある単純なヘッダー/ソース組織のみがあります。

しかし、他の場合(テンプレート化されたオブジェクトはお互いを知っている)、オブジェクトごとに別々の宣言ヘッダーと実装ヘッダーを用意する必要がありました。

ヘッダーを個別のヘッダーに分解するもう1つの理由は、コンパイルを高速化し、解析されるシンボルの量を厳密に必要なものに制限し、インラインメソッドの実装が変更されたときに前方宣言のみを必要とするソースの不必要な再コンパイルを避けるためです。

結論

コードの構成は、可能な限りシンプルにすると同時に、モジュール化する必要があります。ソースファイルに可能な限り配置します。共有する必要があるものだけをヘッダーで公開します。

しかし、テンプレート化されたオブジェクト間で循環的な依存関係が生じる日は、コード組織が単純なヘッダー/ソース組織よりも「おもしろい」になっても驚かないでください...

^ _ ^

50
paercebal

他のすべての回答に加えて、ヘッダーファイルに何を配置しないかを説明します。
using宣言(最も一般的なのはusing namespace std;)が含まれているソースファイルのネームスペースを汚染するため、ヘッダーファイルには表示されません。

16
Adrien Plisson

何もコンパイルしない(バイナリフットプリントがゼロ)がヘッダーファイルに入ります。

変数は何にもコンパイルされませんが、型宣言はコンパイルされます(変数の動作を記述するだけです)。

関数は呼び出しませんが、インライン関数は呼び出します(またはマクロ)。

テンプレートはコードではなく、コードを作成するためのレシピにすぎません。そのため、それらもhファイルに入ります。

6

一般に、宣言はヘッダーファイルに、定義は実装(.cpp)ファイルに配置します。これの例外はテンプレートであり、定義もヘッダーに含める必要があります。

この質問とそれに似た質問は、SO-参照してください C++でヘッダーファイルと.cppファイルがあるのはなぜですか? および C++ヘッダーファイル、コード分離 たとえば。

3
anon

クラスと関数の宣言に加えて、ドキュメント、およびインライン関数/メソッドの定義(別の.inlファイルに配置することを好む人もいます)。

1

主にヘッダーファイルに含まれるクラススケルトンまたは宣言(頻繁に変更されない)

また、cppファイルにはclass implementation(頻繁に変更)が含まれています。

1
Ashish

ヘッダーファイル(.h)は、クラス、構造体およびそのメソッド、プロトタイプなどの宣言用である必要があります。これらのオブジェクトの実装はcppで行われます。

.hで

    class Foo {
    int j;

    Foo();
    Foo(int)
    void DoSomething();
}
0
jose

私は見ることを期待しています:

  • 宣言
  • コメント
  • インラインでマークされた定義
  • テンプレート

本当に答えは、入れないことです:

  • 定義(複数定義されることにつながる可能性があります)
  • 宣言/ディレクティブの使用(ヘッダーを含むすべてのユーザーに強制するため、名前の衝突が発生する可能性があります)
0
jk.

ヘッダー(.h)

  • インターフェイスに必要なマクロとインクルード(可能な限り少ない)
  • 関数とクラスの宣言
  • インターフェースの文書化
  • インライン関数/メソッドの宣言(ある場合)
  • グローバル変数の外部(存在する場合)

ボディ(.cpp)

  • 残りのマクロとインクルード
  • モジュールのヘッダーを含める
  • 関数とメソッドの定義
  • グローバル変数(ある場合)

経験則として、モジュールの「共有」部分を.h(他のモジュールが表示できる必要がある部分)に配置し、「非共有」部分を.cppに配置します

PD:はい、グローバル変数を含めました。私はそれらを何度か使用しましたが、ヘッダーでそれらを定義しないことが重要です。

編集:デビッドのコメントの後に修正

0
Khelben

ヘッダー定義何かですが、実装については何も伝えません。 (この「メタフォア」のテンプレートを除く。

そうは言っても、「定義」をサブグループに分割する必要があります。この場合、2種類の定義があります。

  • 構造の「レイアウト」を定義し、周囲の使用グループが必要とする範囲でのみ通知します。
  • 変数、関数、およびクラスの定義。

今、私はもちろん最初のサブグループについて話しています。

ヘッダーは、残りのソフトウェアが実装を使用できるようにするために、構造のレイアウトを定義するためにあります。実装の「抽象化」として見たいと思うかもしれませんが、それはよく言われますが、この場合には非常によく合っていると思います。

以前のポスターで、プライベートおよびパブリックの使用領域とそのヘッダーを宣言していることが示されているように、これにはプライベート変数とパブリック変数も含まれています。さて、ここではコードの設計には行きたくありませんが、エンドユーザーと実装の間のレイヤーであるため、ヘッダーに何を入れるかを検討する必要があります。

0
Filip Ekberg
  • ヘッダーファイル-開発中にあまり頻繁に変更しないでください->考えて、一度にそれらを記述する必要があります(理想的な場合)
  • ソースファイル-実装中の変更
0
nothrow