ヘッダー/cppファイルでヘッダーを宣言する順序は? 明らかに、後続のヘッダーで必要とされるものはより早くする必要があり、クラス固有のヘッダーはヘッダー スコープではなく cpp スコープ内にある必要がありますが、設定された順序規則/ベスト プラクティスはありますか?
10 に答える
ヘッダー ファイルには、コンパイル可能にするためにすべてのヘッダーを含める必要があります。また、一部のヘッダーの代わりに前方宣言を使用することを忘れないでください。
ソース ファイル内:
- 対応ヘッダファイル
- 必要なプロジェクト ヘッダー
- サードパーティ ライブラリのヘッダー
- 標準ライブラリのヘッダー
- システム ヘッダー
この順序で、独自のライブラリをインクルードするのを忘れたヘッダー ファイルを見逃すことはありません。
グッド プラクティス: すべての .h ファイルには、他の何よりも先にその .h を含む .cpp が必要です。これは、任意の .h ファイルを最初に置くことができることを証明しています。
ヘッダーが実装を必要としない場合でも、その .h ファイルのみを含む .cpp を作成します。
これは、好きな方法で質問に答えることができることを意味します。それらをどの順序で含めるかは問題ではありません。
さらに優れたヒントについては、次の本を試してみてください:大規模な C++ ソフトウェア設計- 非常に高価なのは残念ですが、実際には C++ ソース コード レイアウトのサバイバル ガイドです。
ヘッダー ファイルでは、標準ヘッダーを最初に配置し、次に独自のヘッダーを配置する傾向があります (両方のリストをアルファベット順に並べています)。実装ファイルでは、最初に対応するヘッダー (存在する場合) を配置し、次に標準ヘッダーとその他の依存ヘッダーを配置します。
#define
マクロと;を多用する場合を除いて、順序はほとんど重要ではありません。その場合、定義したマクロが以前に含まれていたものを置き換えないことを確認する必要があります (もちろん、それが必要な場合は除きます)。
この発言について
後続のヘッダーで必要とされるものは、より早くする必要があります
ヘッダーは、その前に含まれている他のヘッダーに依存するべきではありません! ヘッダーが必要な場合は、それらを含めるだけです。ヘッダー ガードは、複数のインクルードを防ぎます。
#ifndef FOO_HEADER_H
#define FOO_HEADER_H
...
#endif
編集
この回答を書いたので、コード内のインクルード ディレクティブの順序を変更しました。現在、私は常に標準化の昇順でヘッダーを配置しようとしています。そのため、プロジェクトのヘッダーが最初に来て、サードパーティ ライブラリのヘッダーが続き、標準のヘッダーが続きます。
たとえば、私のファイルの 1 つが私が作成したライブラリ、Qt、Boost、および標準ライブラリを使用している場合、インクルードを次のように並べます。
//foo.cpp
#include "foo.hpp"
#include <my_library.hpp>
// other headers related to my_library
#include <QtCore/qalgorithms.h>
// other Qt headers
#include <boost/format.hpp> // Boost is arguably more standard than Qt
// other boost headers
#include <algorithms>
// other standard algorithms
my_library.hpp
私がこれを行う理由はstd::copy
、自分のヘッダーで欠落している依存関係を検出するため<algorithm>
です。の後に含めると<algorithm>
、foo.cpp
この不足している依存関係が見過ごされます。それどころか、先ほど示した順序では、コンパイラはstd::copy
が宣言されていないと文句を言うので、修正することができますmy_library.hpp
。
各「ライブラリ」グループでは、インクルード ディレクティブをアルファベット順に並べて、より簡単に見つけられるようにしています。
補足として、ヘッダー ファイル間の依存関係を最大限に制限することもお勧めします。ファイル、特にヘッダー ファイルに含まれるヘッダーはできるだけ少なくする必要があります。実際、含めるヘッダーが多いほど、何かが変更されたときに再コンパイルする必要があるコードが多くなります。これらの依存関係を制限する良い方法は、ヘッダー ファイルで十分な場合が多い前方宣言を使用することです (「前方宣言はいつ使用できますか?」を参照してください)。
Google C ++スタイルガイド、名前とインクルードの順序:
dir2/foo2.hにあるものを実装またはテストすることを主な目的とするdir/foo.ccで、次のようにインクルードを注文します。
- dir2 / foo2.h(推奨される場所-以下の詳細を参照)。
- Cシステムファイル。
- C++システムファイル。
- 他のライブラリの.hファイル。
- プロジェクトの.hファイル。
以前はアルファベット順に並べていました(見つけやすい)
「方法」は明らかではありませんが、「何」は明白です。あなたの目標は、ヘッダーファイルをインクルードする順序が決して重要ではないことを確認することです(そして私は「決して!」を意味しません)。
ヘッダーファイルの1つだけを含むcppファイル(ヘッダーファイルごとに1つ)をビルドするときに、ヘッダーファイルがコンパイルされるかどうかをテストすることをお勧めします。
.cpp ファイルの場合、クラスのヘッダーまたは最初に実装するものをインクルードする必要があるため、このヘッダーに一部のインクルードがない場合をキャッチします。その後、ほとんどのコーディング ガイドラインでは、システム ヘッダーが最初に含まれ、プロジェクト ヘッダーが 2 番目に含まれる傾向があります。たとえば、Google C++ スタイル ガイドなどです。
これは依存関係の問題であり、ヘッダーに何を入れるかに大きく依存します。実際には、これについて非常に悪名高く、最小化してインクルードを厳密に保つことができますが、最終的にはインクルージョン ガードを使用したいシナリオに遭遇することになります。
#ifndef MY_HEADER_H
#define MY_HEADER_H
//...
#endif
問題は最初はそれほど明白ではありませんが、ソフトウェアの複雑さが増すにつれて、依存関係も大きくなります。あなたはうまくやることができ、それについて賢くすることができますが、大規模な C++ プロジェクトは一般的にインクルードでいっぱいです。試すことはできますが、できることは限られています。ですから、インクルードについてよく考えてください。はい! ただし、ある時点で循環依存関係が発生することはほぼ確実です。そのため、包含ガードが必要です。
ヘッダーに他のヘッダーが必要な場合は、そのヘッダーにそれらを含めるだけです。
ポインターまたは参照を渡し、可能な場合は前方宣言するようにコードを構造化してください。
実装では、それを定義するヘッダーを最初にリストする必要があります (ただし、Visual Studio で pch を使用している場合は、stdafx が最初に表示されます)。
私は通常、必要に応じてそれらをリストします。
次の規則が最も便利であることがわかりました。
モジュール.cpp:
// this is the header used to trigger inclusion of precompiled headers
#include <precompiled.h>
// this ensures that anything that includes "module.h" works
#include "module.h"
// other headers, usually system headers, the project
重要なことは、モジュールのヘッダーをプリコンパイルされていない最初のヘッダーとして配置することです。これにより、「module.h」に予期しない依存関係がなくなります。
ディスク アクセス時間が遅い大規模なプロジェクトで作業している場合、ビルド時間を短縮するためにこのスタイルが使用されているのを見てきました。
モジュール.cpp:
// this is the header used to trigger inclusion of precompiled headers
#include <precompiled.h>
// this ensures that anything that includes "module.h" works
#include "module.h"
// other headers, usually system headers, the project
#if !defined _OTHER_MODULE_GUARD_
#include "other_module.h"
#endif
#if !defined _ANOTHER_MODULE_GUARD_
#include "another_module.h"
#endif
少し冗長ですが、ヘッダーが既に含まれている場合はヘッダーが検索/開かれないため、ディスクのシークを節約できます。ガード チェックがないと、コンパイラはヘッダー ファイルを探して開き、ファイル全体を解析して、最終的#ifdef
にファイル全体を出力します。