コードを複数のファイルに分割するとき、正確に何を.hファイルに入れ、何を.cppファイルに入れるべきですか?
.hpp
ファイルに入れられ、C宣言はファイルに入れられると思い.h
ます。これは、CコードとC ++コード(たとえば、Cのレガシーモジュール)を混在させる場合に非常に役立ちます。
コードを複数のファイルに分割するとき、正確に何を.hファイルに入れ、何を.cppファイルに入れるべきですか?
.hpp
ファイルに入れられ、C宣言はファイルに入れられると思い.h
ます。これは、CコードとC ++コード(たとえば、Cのレガシーモジュール)を混在させる場合に非常に役立ちます。
回答:
ヘッダーファイル(.h
)は、複数のファイルで必要になる情報を提供するように設計されています。クラス宣言、関数プロトタイプ、および列挙などは通常、ヘッダーファイルに格納されます。つまり、「定義」。
コードファイル(.cpp
)は、1つのファイルでのみ知る必要がある実装情報を提供するように設計されています。一般に、関数本体、および他のモジュールからアクセスされるべき/決してアクセスされないであろう内部変数は、.cpp
ファイルに属しているものです。つまり、「実装」。
「これを変更した場合、他のファイルのコードを変更して、コンパイルできるようにする必要がありますか」というのはどこにあるのかを判断するための最も簡単な質問です。答えが「はい」の場合、それはおそらくヘッダーファイルに属しています。答えが「いいえ」の場合、おそらくコードファイルに含まれています。
export
)。#1を回避する唯一の方法はPIMPLです。#2 export
はサポートされていれば可能であり、c ++ 0xとextern
テンプレートを使用して可能である可能性があります。IMO、c ++のヘッダーファイルはその有用性の多くを失います。
事実は、C ++では、これはCヘッダー/ソース構成よりもやや複雑です。
コンパイラーは、ヘッダーが適切に組み込まれた1つの大きなソース(.cpp)ファイルを参照します。ソースファイルは、オブジェクトファイルにコンパイルされるコンパイル単位です。
あるコンパイルユニットが別のコンパイルユニットの実装に関する情報を必要とする可能性があるためです。したがって、たとえば、関数の実装を1つのソースに記述し、この関数の宣言を、それを使用する必要がある別のソースに書き込むことができます。
この場合、同じ情報のコピーが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;
)は、それが含まれているソースファイルの名前空間を汚染するため、ヘッダーファイルに表示しないでください。 。
using
ヘッダーのグローバル名前空間にものを取り込むために決して使用しないでください。
static inline
C99では、内部リンケージとテンプレートを組み合わせるとどうなるかと関係があるため、C ++では匿名名前空間のインライン関数をお勧めします。Anon名前空間を使用すると、外部リンケージを維持しながら機能を「隠す」ことができます。
何もコンパイルしないもの(ゼロバイナリフットプリント)はヘッダーファイルに入ります。
変数は何もコンパイルされませんが、型宣言はコンパイルされます(変数の動作を説明するだけです)。
関数はそうしませんが、インライン関数は(またはマクロ)します。なぜなら、それらは呼び出された場所でのみコードを生成するからです。
テンプレートはコードではなく、コードを作成するためのレシピにすぎません。そのため、これらもhファイルに入れられます。
通常、宣言はヘッダーファイルに、定義は実装(.cpp)ファイルに配置します。これの例外はテンプレートであり、定義もヘッダーに含める必要があります。
この質問とそれに類似した質問は、SOで頻繁に尋ねられています。C++にヘッダーファイルと.cppファイルがある理由を参照してください。およびC ++ヘッダーファイル、たとえばコード分離。
クラスと関数の宣言とドキュメント、およびインラインの関数/メソッドの定義(別の.inlファイルに配置する方を好む場合もあります)。
主にヘッダーファイルにはクラスのスケルトンまたは宣言が含まれます(頻繁に変更されません)
cppファイルにはクラス実装が含まれます(頻繁に変更されます)。
ヘッダーは何かを定義しますが、実装については何も伝えません。(この「メタフォア」のテンプレートを除く。
そうは言っても、「定義」をサブグループに分割する必要があります。この場合、2つのタイプの定義があります。
今、私はもちろん最初のサブグループについて話しています。
ヘッダーは、残りのソフトウェアが実装を使用できるようにするために、構造のレイアウトを定義するためにあります。あなたはそれをあなたの実装の「抽象化」として見たいかもしれませんが、それは大げさに言われますが、私はそれはこの場合かなりよく合うと思います。
以前のポスターでプライベートおよびパブリックの使用領域とそれらのヘッダーを宣言することについて述べ、示したように、これにはプライベートおよびパブリック変数も含まれます。ここで、ここではコードの設計については触れたくありませんが、ヘッダーに何を入れるかを検討することをお勧めします。これは、エンドユーザーと実装の間のレイヤーであるためです。
ヘッダー(.h)
本文(.cpp)
経験則として、モジュールの「共有」部分(他のモジュールが見ることができる必要のある部分)を.hに配置し、「非共有」部分を.cppに配置します。
PD:はい、私はグローバル変数を含めました。私は何度かそれらを使用しましたが、ヘッダーでそれらを定義しないことが重要です。そうしないと、それぞれが独自の変数を定義する多くのモジュールが得られます。
編集:デビッドのコメントの後に変更