2012-01-20 145 views
3

GoogleのC++スタイルガイドsays「前方宣言で十分である場合は#includeを使用しないでください。フォワード宣言または自己完結型ヘッダー?

C++ Coding Standards(Sutter and Alexandrescu)、項目23は、「ヘッダファイルを自立的にする」責任感を持って行動する:あなたが書いた各ヘッダは、その内容に依存するヘッダを含めることによってコンパイル可能なスタンドアロンであることを確認してください。

どの戦略が最適ですか?

+0

私はグーグルが好きです。複雑さを軽減します。 –

+5

両方を行うことはかなり可能です。 –

+2

私は彼らがお互いに矛盾しているとは思わない。両方を適用すると良いアイデアのように思える – Arvid

答えて

3

アイテム#22のSutterとAlexandrescuは、「過度に依存しないでください:フォワード宣言で#定義を含めません」と言っています。

個人的には、私はこの声明に同意します。私のクラスAでクラスBの機能を使用していない場合や、クラスBのオブジェクトをインスタンス化していない場合、クラスBの作成方法を知る必要はありません。私はそれが存在することを知る必要があります。

前方宣言はまた、循環依存関係を壊すのに便利です...

編集:私もたくさんのようなマークBが指摘観測:ので、あなたがファイルa.hppが含まれていないことを時々起こりますそれはすでに含まれているb.hppファイルに含まれています。フォワード宣言で十分でしたが、a.hppをインクルードすることを選択しました。 b.hppで定義されている関数の使用をやめると、コードはもうコンパイルされません。 b.hppのプログラマーが前方宣言を使用していた場合、コード内にa.hppを含めるので、これは起こりませんでした...

+0

良い発見!それは誤った二分法を解決します。 – traal

1

ここでは、おそらくサッターとアレクサンドルシュが正しいでしょう。私は前方に何らかの理由でbar.hに常駐class Foo;を宣言し、bar.hが含まれていない場合は、幸運は(コードベースが大きい場合は特に)Fooの宣言を見つけよう。

2

私は両方のステートメントが正しいと言います。ポインタまたは一部のデータ型への参照のみを含むヘッダーファイルがある場合は、前方宣言のみが必要です。

ヘッダーに特定のタイプのオブジェクトが含まれている場合、そのタイプが定義されているヘッダーを含める必要があります。 SutterとAlexandrescuの助言は、必要な型定義を含めることによって、そのような参照を解決するヘッダーの消費者に頼らないように指示しています。

また、Pimpl IdiomCompilation firewalls(またはC++11 version)もチェックしてください。

+0

ありがとう、今私は何が欠けていたかを参照してください。ヘッダファイルは、#includeされていないものの前方宣言でコンパイルされることがよくあります。 #includeが必要なのはコードファイルだけです。 – traal

1

その他の回答に記載されている内容に加えて、は、相互依存関係のためにに前方宣言を使用する必要があるケースがあります。

FWIWでは、型名が必要な場合は、通常は前方に宣言します。関数宣言の場合は、一般的にそれを含めます(非メンバ関数はまれであり、メンバ関数を含める必要があるため、これらのケースはまれです)。

extern "C"関数の場合、never forward宣言は、引数型を混乱させるかどうかをリンカが教えてくれないためです。

2

私は彼らがどちらもまったく同じことを言っていると信じています。

はあなたのあなたのヘッダファイル参照によってBarを取る方法があるとしますが、この方法は、ソース・ファイルで定義されています。ヘッダーの前方宣言は、ヘッダーがスタンドアロンでコンパイルするためには明らかに十分です。

ここで、ユーザーコードを見てみましょう。クライアントが他の場所から転送された参照を渡しているだけの場合は、Barという定義は必要なく、すべて正常です。他の方法でBarを使用している場合、そのソースファイルは、BarNOTのヘッダーのインクルードの使用を強制するものです。実際には、不要なBarインクルードをヘッダーに含めると、クライアントがインクルードを必要としなくなり、それを削除すると、他のBarコードが突然元のソースファイルに正しくインクルードされないため、コードが機能しなくなります。

あなたのヘッダーがstd::stringを使用しているとします。その場合、ヘッダがスタンドアロンでコンパイルされるためには、両方のガイドラインには、forward宣言ではできないのでgoogle、スタンドアロンでコンパイルするためにはSutterとAlexandrescuが指示する<string>を含める必要があります。

1

これらの2つの推奨事項は、完全にと互換性があります。あなたは同時にそれらの両方に従うことができます。彼らは決して相互に排他的ではありません - それは "どちらか"の状況ではありません。

ヘッダはそれが必要とされる全てである場合、前方宣言を使用し、さらにコンパイルスタンドアロンであってもよい。上記の例で

// Foo.hpp 
class Bar; // Forward declaration 

class Foo { 
public: 
    void doSomethingFancy(); 

private: 
    Bar *bar; 
}; 

Foo.hppBarクラスも自己完結でありますBar.hppヘッダーを含めるのではなく、前方宣言を使用して宣言します。

4

依存関係管理はC++では非常に重要です。ヘッダーファイルを変更する場合、このヘッダーファイルに応じてすべての翻訳単位をコンパイルする必要があります。これは非常に高価になる可能性があります。結果として、ヘッダーファイルは、インクルードする必要のないものは含まれていないという意味で最小限に抑えられます。これがGoogleのアドバイスです。

特定のコンポーネントが必要な場合は、コンポーネントのヘッダーを含める必要があります。ただし、何も含める必要はありませんが、コンポーネントのには、の宣言があります。つまり、各ヘッダファイルは他のものを含めることなくコンパイルする必要があります。これはハーブとアンドレイのアドバイスです。このは、のみが宣言を取得するために適用されます。これらの宣言のいずれかを使用し、別のコンポーネントが必要な場合は、このコンポーネントのヘッダーファイルも含める必要があります。

しかし、これらの2つのアドバイスは一緒になります。彼らはどちらも非常に価値があり、妥協することなく守らなければなりません。これは、基本的には、宣言されたクラスのみが必要な場合は、そのヘッダーを含むクラスを宣言することをお勧めします。つまり、クラスが宣言、ポインタ定義または参照定義、パラメータリスト、または戻り値の型だけである場合は、と宣言されていれば十分です。あなたがクラスについてもっと知る必要がある場合、例えばこれは、定義されているクラスの基底またはメンバであるか、オブジェクトが使用されているためです。インライン関数では、定義が必要です。効果的には、メンバーのいずれか、またはクラスのサイズを知る必要がある場合は、クラスの定義が必要です。

最初のクラスのテンプレート宣言だけがデフォルトの引数を定義できます。ただし、クラステンプレートは複数回宣言できます。クラステンプレートを宣言している間にヘッダーファイルを最小限にするには、クラステンプレートとデフォルトの引数のみを宣言する関連するクラステンプレート用の特別な転送ヘッダーが必要です。しかし、これは実装土地への道です...

関連する問題