ヘッダーファイルに何を含めるべきではありませんか?[閉まっている]


71

ヘッダーファイルに絶対に含まれてはならないものは何ですか?

たとえば、多くの定数を持つ文書化された業界標準形式で作業している場合、ヘッダーファイルで定義することをお勧めします(その形式のパーサーを作成している場合)。

ヘッダーファイルにはどの関数を挿入する必要がありますか?
してはいけない機能は何ですか?


1
短く痛みがありません:複数のモジュールで必要な定義と宣言。
-ott--

21
この質問に「広すぎる」というマークを付けて締めくくるのは、中程度の恥ずかしい絶対的なやり過ぎです。この質問は、私が探しているものを正確に尋ねます-質問は整形式であり、非常に明確な質問をします:ベストプラクティスは何ですか?これがソフトウェアエンジニアリングにとって「広すぎる」場合、このフォーラム全体を閉鎖することもできます。
Gewure

TL; DR。C ++の場合、Bjarne Stroustrup(その作成者)によって書かれた「The C ++ Programming Language」の第4版では、セクション15.2.2にヘッダーに含めるべきものとすべきでないものが記述されています。質問をCにタグ付けしたことは知っていますが、いくつかのアドバイスも適用できます。これはいい質問だと思う
...-horro

回答:


57

ヘッダーに入れるもの:

  • #includeヘッダーがソースファイルに含まれている場合にヘッダーをコンパイル可能にするために必要なディレクティブの最小限のセット。
  • 共有する必要があり、プリプロセッサを介してのみ達成できるもののプリプロセッサシンボル定義。Cであっても、プリプロセッサシンボルは最小限に抑えるのが最適です。
  • ヘッダー本体の構造定義、関数プロトタイプ、およびグローバル変数宣言をコンパイル可能にするために必要な構造の前方宣言。
  • 複数のソースファイル間で共有されるデータ構造と列挙の定義。
  • 定義がリンカーから見える関数と変数の宣言。
  • インライン関数定義。ただし、ここで注意してください。

ヘッダーに含まれないもの:

  • 無償の#includeディレクティブ。これらの無償のインクルードは、再コンパイルする必要のないものの再コンパイルを引き起こし、時にはシステムがコンパイルできないようにすることができます。#includeヘッダー自体が他のヘッダーファイルを必要としない場合は、ヘッダーにファイルを入れないでください。
  • プリプロセッサ以外の何らかのメカニズム、任意のメカニズムによって意図を達成できるプリプロセッサシンボル。
  • たくさんの構造定義。それらを別々のヘッダーに分割します。
  • 追加のを必要とする関数、#include変更される可能性のある関数、または大きすぎる関数のインライン定義。それらのインライン関数は、ファンアウトがあればほとんどありません。ファンアウトがある場合は、ヘッダーで定義されたものにローカライズする必要があります。

#includeステートメントの最小限のセットは何ですか?

これは重要な問題であることがわかりました。TL; DR定義:ヘッダーファイルには、直接使用される各タイプを直接定義するヘッダーファイル、または問題のヘッダーファイルで使用される各関数を直接宣言するヘッダーファイルを含める必要がありますが、他のものを含めることはできません。ポインターまたはC ++参照型は、直接使用とは見なされません。前方参照が推奨されます。

無償の#includeディレクティブのための場所があり、これは自動テスト中です。ソフトウェアパッケージ内のすべてのヘッダーファイルに対して、次を自動的に生成してコンパイルします。

#include "path/to/random/header_under_test"
int main () { return 0; }

コンパイルはクリーン(つまり、警告やエラーがない状態)でなければなりません。不完全な型または不明な型に関する警告またはエラーは、テスト中のヘッダーファイルに欠落している#includeディレクティブや転送宣言がないことを意味します。注:テストに合格したからといって、#includeディレクティブのセットが十分であることを意味するわけではありません。


したがって、Aと呼ばれる構造体を定義するライブラリがあり、Bと呼ばれるこのライブラリがその構造体を使用し、ライブラリBがプログラムCによって使用される場合、ライブラリAのヘッダーファイルをライブラリBのメインヘッダーに含めるか、ただ宣言するだけですか?ライブラリAはコンパイルされ、コンパイル中にライブラリBとリンクされます。
-MarcusJ

@MarcusJ-「ヘッダー属さないもの」で最初にリストしたのは、無償の#includeステートメントでした。ヘッダーファイルBがヘッダーファイルAの定義に依存しない場合は、ヘッダーファイルBにヘッダーファイルAを#includeしないでください。ヘッダーファイルは、サードパーティの依存関係またはビルド手順を指定する場所ではありません。これらは、トップレベルのreadmeファイルなど、他の場所に移動します。
デビッドハンメン

1
@MarcusJ-あなたの質問に答えようとして、答えを更新しました。質問に対する答えは1つではないことに注意してください。いくつかの極端な例で説明します。ケース1:ライブラリBがライブラリAの機能を直接使用する唯一の場所は、ライブラリBのソースファイルです。ケース2:ライブラリBは、ライブラリAで定義された型や関数を直接使用するライブラリBのヘッダーファイルを持つ、ライブラリAの機能の薄い拡張です。ケース1では、ライブラリAを公開する理由はありませんライブラリBのヘッダー。ケース2では、この公開はほとんど必須です。
デビッドハンメン

ええ、それはケース2です。申し訳ありませんが、ライブラリBのヘッダーでライブラリAで宣言された型を使用しているという事実を無視してコメントをスキップしました。転送宣言できると考えていましたが、うまくいかないと思います。更新していただきありがとうございます。
-MarcusJ

ヘッダーファイルに定数を追加することは大丈夫ですか?
mding5692

15

すでに言われたことに加えて。

Hファイルには常に以下を含める必要があります。

  • ソースコードのドキュメント!!! 少なくとも、さまざまなパラメーターの目的と関数の戻り値は何ですか。
  • ヘッダーガード、#ifndef MYHEADER_H #define MYHEADER_H ... #endif

Hファイルには次のものを含めないでください。

  • 任意の形式のデータ割り当て。
  • 関数定義。インライン関数は、まれに例外になる場合があります。
  • ラベル付けされたものstatic
  • アプリケーションの他の部分とは無関係のTypedef、#define、または定数。

(また、どこでも、どこでも定数でないグローバル/外部変数を使用する理由はありませんが、それは別の投稿の議論です。)


1
あなたが強調したことを除いて、私はすべてに同意します。ライブラリを作成している場合は、はい、ドキュメントまたはライブラリのユーザーを文書化する必要があります。社内プロジェクトでは、わかりやすい変数名と関数名を使用する場合、ヘッダーをドキュメントで乱雑にする必要はありません。
-martiert

5
@martiert私も「コードにそれ自体を語らせる」学校です。それでも、自分以外のだれもそれらを使用しない場合でも、少なくとも常に関数をドキュメント化する必要があります。特に興味深いのは、関数にエラー処理がある場合、どのエラーコードが返され、どの条件で失敗するかということです。関数が失敗した場合、パラメーター(バッファー、ポインターなど)はどうなりますか?非常に関連性の高いもう1つのことは、呼び出し元に何かを返すポインターパラメーターかどうか、つまり、割り当てられたメモリを期待するかどうかです。->

1
関数内でどのエラー処理が実行され、何が実行されないかは、呼び出し元に明らかです。関数が割り当てられたバッファを予期する場合、ほとんどの場合、範囲外チェックも呼び出し元に任せます。関数が実行される別の関数に依存する場合、これを文書化する必要があります(すなわち、link_list_add()の前にlink_list_init()を実行します)。最後に、関数にファイル、スレッド、タイマーなどの「副作用」がある場合は、ドキュメントに記載する必要があります。->

1
「ソースコードのドキュメント」はここでは広すぎるかもしれませんが、これは本当にソースコードに属します。入力と出力、事前条件と事後条件、および副作用を伴う「使用法のドキュメント」は、叙事詩ではなく簡潔な形で間違いなくそこに行くべきです。
セキュア

2
少し遅れていますが、ドキュメントの場合は+1です。なぜこのクラスが存在するのですか?コードはそれ自体を語っていませ。この機能は何をしますか?RTFC(すばらしい.cppファイルを読む)はわいせつな4文字の頭字語です。理解するためにRTFCを使用する必要はありません。ヘッダー内のプロトタイプは、いくつかの抽出可能なコメント(たとえば、doxygen)に、引数が何であり、関数が何をするかを要約する必要があります。なぜこのデータメンバが存在し、何が含まれ、メートル、フィート、またはハロン単位の値ですか?これも(抽出可能な)コメントが存在するための別の主題です。
デビッドハンメン

4

私はおそらく決して「決して」とは言いませんが、データとコードが解析されるときにそれらを生成するステートメントは、.hファイルに入れないでください。

マクロ、インライン関数、およびテンプレートは、データまたはコードのように見える場合がありますが、解析されるためコードが生成されるのではなく、使用されるときに生成されます。これらのアイテムは多くの場合、複数の.cまたは.cppで使用する必要があるため、.hに属します。

私の見解では、ヘッダーファイルには、対応する.cまたは.cppへの最小限の実用的なインターフェイスが必要です。インターフェイスには、#defines、class、typedef、struct定義、関数プロトタイプ、およびグローバル変数のあまり好ましくないextern定義を含めることができます。ただし、宣言が1つのソースファイルのみで使用されている場合は、おそらく.hから除外し、代わりにソースファイルに含める必要があります。

意見の相違がある人もいるかもしれませんが、.hファイルに関する私の個人的な基準は、コンパイルする必要がある他のすべての.hファイルを#includeすることです。場合によっては、これは大量のファイルになる可能性があるため、インクルードファイルの大きなツリーを含めることなく、クラスのオブジェクトへのポインターを使用できるようにするクラスへの前方宣言などの外部依存関係を減らすための効果的な方法がいくつかあります。


3

ヘッダーファイルの構成は次のとおりです。

  • 型と定数の定義
  • 外部オブジェクト宣言
  • 外部関数宣言

ヘッダーファイルにはオブジェクト定義を含めず、タイプ定義とオブジェクト宣言のみを含める必要があります。


インライン関数の定義はどうですか?
コス

インライン関数が1つのCモジュール内でのみ使用される「ヘルパー」関数である場合、その.cファイルのみに配置します。インライン関数が2つ以上のモジュールから見えるようにする必要がある場合は、ヘッダーファイル内に配置します。
theD

また、ライブラリの境界を越えて関数を表示する必要がある場合は、インライン化しないでください。ライブラリを使用するすべての人が、物事を変更するたびに再コンパイルする必要があります。
ドナルドフェローズ

@DonalFellows:それはバックハンドソリューションです。より良いルール:頻繁に変更される可能性のあるものをヘッダーに入れないでください。関数にファンアウトがなく、基礎となるデータ構造が変更された場合にのみ変更される明確な定義がある場合、ヘッダーに短い小さな関数をインライン化しても問題はありません。基礎となる構造定義が変更されたために関数定義が変更された場合、すべてを再コンパイルする必要がありますが、構造定義が変更されたため、とにかく再コンパイルする必要があります。
デビッドハメン

0

解析時にデータとコードを生成するステートメントは、.hファイルに含めるべきではありません。私の観点からすれば、ヘッダーファイルには、対応する.cまたはへの最小限の実用的なインターフェイスのみが必要.cppです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.