コードを複数のファイルに分割する場合、正確に何が.hファイルに、何が.cppファイルに入力されるべきでしょうか?
ヘッダーファイル(.h
)は、複数のファイルで必要になる情報を提供するように設計されています。通常、クラス宣言、関数プロトタイプ、列挙などはヘッダーファイルに格納されます。一言で言えば、「定義」。
コードファイル(.cpp
)は、1つのファイルでのみ知る必要がある実装情報を提供するように設計されています。一般的に、関数本体、および他のモジュールがアクセスすべき/決してアクセスしない内部変数は、.cpp
ファイル。一言で言えば、「実装」。
どこに属しているかを判断するための最も簡単な質問は、「これを変更した場合、他のファイルのコードを変更して再度コンパイルする必要がありますか?」です。答えが「はい」の場合、おそらくヘッダーファイルに属します。答えが「いいえ」の場合、おそらくコードファイルに属します。
事実は、C++では、これはCヘッダー/ソース構成よりもやや複雑です。
コンパイラは、ヘッダーが適切に含まれている1つの大きなソース(.cpp)ファイルを確認します。ソースファイルは、オブジェクトファイルにコンパイルされるコンパイル単位です。
あるコンパイル単位には、別のコンパイル単位の実装に関する情報が必要になる可能性があるためです。したがって、たとえば、あるソースで関数の実装を記述し、それを使用する必要がある別のソースでこの関数の宣言を記述することができます。
この場合、同じ情報のコピーが2つあります。どちらが悪か...
解決策は、いくつかの詳細を共有することです。実装はソースに残る必要がありますが、関数などの共有シンボルの宣言、または構造、クラス、列挙などの定義を共有する必要があります。
ヘッダーは、これらの共有された詳細を配置するために使用されます。
複数のソース間で共有する必要があるものの宣言をヘッダーに移動します
C++には、共有する必要があるため、ヘッダーに追加できるものがいくつかあります。
共有実装を含め、共有する必要があるものすべてにヘッダーを移動します
はい。実際、「ヘッダー」内(つまり、ソース間で共有される)になる可能性のあるものはたくさんあります。
複雑になり、場合によっては(シンボル間の循環依存関係)、1つのヘッダーに収めることができなくなります。
これは、極端な場合、次のことができることを意味します。
テンプレート化された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つの理由は、コンパイルを高速化し、解析されるシンボルの量を厳密に必要なものに制限し、インラインメソッドの実装が変更されたときに前方宣言のみを必要とするソースの不必要な再コンパイルを避けるためです。
コードの構成は、可能な限りシンプルにすると同時に、モジュール化する必要があります。ソースファイルに可能な限り配置します。共有する必要があるものだけをヘッダーで公開します。
しかし、テンプレート化されたオブジェクト間で循環的な依存関係が生じる日は、コード組織が単純なヘッダー/ソース組織よりも「おもしろい」になっても驚かないでください...
^ _ ^
他のすべての回答に加えて、ヘッダーファイルに何を配置しないかを説明します。using
宣言(最も一般的なのはusing namespace std;
)が含まれているソースファイルのネームスペースを汚染するため、ヘッダーファイルには表示されません。
何もコンパイルしない(バイナリフットプリントがゼロ)がヘッダーファイルに入ります。
変数は何にもコンパイルされませんが、型宣言はコンパイルされます(変数の動作を記述するだけです)。
関数は呼び出しませんが、インライン関数は呼び出します(またはマクロ)。
テンプレートはコードではなく、コードを作成するためのレシピにすぎません。そのため、それらもhファイルに入ります。
一般に、宣言はヘッダーファイルに、定義は実装(.cpp)ファイルに配置します。これの例外はテンプレートであり、定義もヘッダーに含める必要があります。
この質問とそれに似た質問は、SO-参照してください C++でヘッダーファイルと.cppファイルがあるのはなぜですか? および C++ヘッダーファイル、コード分離 たとえば。
クラスと関数の宣言に加えて、ドキュメント、およびインライン関数/メソッドの定義(別の.inlファイルに配置することを好む人もいます)。
主にヘッダーファイルに含まれるクラススケルトンまたは宣言(頻繁に変更されない)
また、cppファイルにはclass implementation(頻繁に変更)が含まれています。
ヘッダーファイル(.h)は、クラス、構造体およびそのメソッド、プロトタイプなどの宣言用である必要があります。これらのオブジェクトの実装はcppで行われます。
.hで
class Foo {
int j;
Foo();
Foo(int)
void DoSomething();
}
私は見ることを期待しています:
本当に答えは、入れないことです:
ヘッダー(.h)
ボディ(.cpp)
経験則として、モジュールの「共有」部分を.h(他のモジュールが表示できる必要がある部分)に配置し、「非共有」部分を.cppに配置します
PD:はい、グローバル変数を含めました。私はそれらを何度か使用しましたが、ヘッダーでそれらを定義しないことが重要です。
編集:デビッドのコメントの後に修正
ヘッダー定義何かですが、実装については何も伝えません。 (この「メタフォア」のテンプレートを除く。
そうは言っても、「定義」をサブグループに分割する必要があります。この場合、2種類の定義があります。
今、私はもちろん最初のサブグループについて話しています。
ヘッダーは、残りのソフトウェアが実装を使用できるようにするために、構造のレイアウトを定義するためにあります。実装の「抽象化」として見たいと思うかもしれませんが、それはよく言われますが、この場合には非常によく合っていると思います。
以前のポスターで、プライベートおよびパブリックの使用領域とそのヘッダーを宣言していることが示されているように、これにはプライベート変数とパブリック変数も含まれています。さて、ここではコードの設計には行きたくありませんが、エンドユーザーと実装の間のレイヤーであるため、ヘッダーに何を入れるかを検討する必要があります。