ヘッダーファイルは実際に良いですか?[閉まっている]


20

ヘッダーファイルは、クラス内のすべての関数とデータメンバーの「概要」を提供するため、C ++ソースファイルを参照するときに役立ちます。他の多くの言語(Ruby、Python、Javaなど)にこのような機能がないのはなぜですか?これは、C ++の冗長性が役立つ領域ですか?


私はCとC ++のツールに精通していませんが、私が働いたほとんどのIDE /コードエディターには「構造」または「概要」ビューがあります。
マイケルK 14年

1
ヘッダーファイルは良くありません-それらは必要です!-SOのこの回答もご覧ください:stackoverflow.com/a/1319570/158483
ピート

回答:


31

ヘッダーファイルの本来の目的は、Cでのシングルパスコンパイルとモジュール化を可能にすることでした。使用する前にメソッドを宣言することにより、シングルコンパイルパスのみが許可されました。この時代は、パワフルなコンピューターが問題なくマルチパスコンパイルを行うことができ、時にはC ++コンパイラーよりも高速であったため、長い間過ぎ去っています。

C ++との後方互換性は、ヘッダーファイルを保持するために必要でしたが、ヘッダーファイルの上に多くを追加したため、非常に問題のある設計になりました。FQAの詳細。

モジュール性のために、モジュール内のコードに関するメタデータとしてヘッダーファイルが必要でした。例えば。どのメソッド(およびC ++クラス)がどのライブラリで利用可能か。コンパイル時間が高価だったため、開発者にこれを書いてもらうのは明らかでした。最近では、コンパイラーがコード自体からこのメタデータを生成しても問題ありません。Javaおよび.NET言語は通常これを行います。

だから ヘッダーファイルは適切ではありません。コンパイラとリンカーを別々のフロッピーに配置する必要があり、コンパイルには30分かかりました。今日、彼らは邪魔をするだけで、悪いデザインの兆候です。


8
私はあなたが書いたものの大部分に同意しますが、最後の段落は物事を少し簡単にします。ヘッダーファイルは、引き続きパブリックインターフェイスを定義するための有用な目的を果たします。
ベンジャミンバニエ

3
@honkそれは私の質問で得ていたものです。しかし、現代のIDE(ElYusubovによって提案された)はこれを否定していると思います。
マットFichman

5
C ++委員会がモジュールに取り組んでおり、CとC ++がヘッダーなしで、またはより効率的な方法でヘッダーを使用して動作できるようになります。それはこの答えをより公平にするでしょう。
クライム

2
@MattFichman:ヘッダーファイルの生成は1つのことです(ほとんどのプログラマーのエディター/ IDEは何らかの方法で自動的に行うことができます)が、パブリックAPIのポイントは、実際の人間によって効果的に定義および設計される必要があるということです。
ベンジャミンバニエ

2
@honkはい。ただし、これを別のファイルで定義する理由はありません。実装と同じコードにすることができます。C#がやっているように。
陶酔

17

ドキュメントの形式としては役に立つかもしれませんが、ヘッダーファイルを取り巻くシステムは非常に非効率的です。

Cは、各コンパイルパスが単一のモジュールを構築するように設計されました。各ソースファイルは、コンパイラの個別の実行でコンパイルされます。一方、ヘッダーファイルは、それらを参照する各ソースファイルのコンパイル手順に挿入されます。

これは、ヘッダーファイルが300個のソースファイルに含まれている場合、プログラムのビルド中に300回別々に解析およびコンパイルされることを意味します。まったく同じ結果で、何度も繰り返します。これは膨大な時間の浪費であり、CおよびC ++プログラムのビルドに非常に長い時間がかかる主な理由の1つです。

すべての現代言語は、この非合理的な非効率性を意図的に回避しています。代わりに、通常コンパイルされた言語では、必要なメタデータがビルド出力内に格納され、コンパイルされたファイルがコンパイルされたファイルに含まれるものを記述する一種のクイックルックアップ参照として機能できるようにします。ヘッダーファイルのすべての利点は、追加作業なしで自動的に作成されます。

インタープリター言語では、ロードされるすべてのモジュールはメモリに残ります。一部のライブラリを参照または含めるか、必要とすると、関連するソースコードが読み取られてコンパイルされ、プログラムが終了するまで常駐します。他にも必要な場合は、既にロードされているため追加の作業はありません。

どちらの場合でも、言語のツールを使用して、このステップで作成されたデータを「ブラウズ」できます。通常、IDEには何らかのクラスブラウザがあります。また、言語にREPLがある場合は、ロードされたオブジェクトのドキュメントサマリーを生成するためにも頻繁に使用できます。


5
厳密にはそうではありません-ほとんどのコンパイラーはヘッダーをプリコンパイルします。ヘッダーを次のコンパイル単位に含めることは、プリコンパイル済みコードのブロックを見つけることより悪くありません。たとえば、コンパイルするすべてのC#ファイルに対してsystem.data.typesを繰り返し「ビルド」する.NETコードと違いはありません。C / C ++プログラムに時間がかかるのは、実際にコンパイル(および最適化)されているためです。.NETプログラムに必要なのは、ILに解析されることだけで、ランタイムはJITを介して実際のコンパイルを行います。ただし、C#コードの300個のソースファイルもコンパイルにかなりの時間がかかります。
gbjbaanb

7

関数やデータメンバーのファイルを参照することの意味は明確ではありません。ただし、ほとんどのIDEには、クラスを参照してクラスデータメンバーを表示するためのツール用意されています

たとえば、次のVisual StudioがありClass ViewかつObject browserそれがうまくあなたの要求された機能を提供します。次のスクリーンショットのように。

ここに画像の説明を入力してください

ここに画像の説明を入力してください


4

ヘッダーファイルのもう1つの欠点は、プログラムがインクルードの順序に依存することであり、プログラマは正確性を確保するために追加の手順を実行する必要があります。

これは、Cのマクロシステム(C ++に継承)と相まって、多くの落とし穴と混乱を招く状況につながります。

たとえば、あるヘッダーがマクロを使用して何らかのシンボルを定義し、別のヘッダーが関数の名前などの別の方法でシンボルを使用した場合、包含順序は最終結果に大きく影響します。そのような例はたくさんあります。


2

ヘッダーファイルは、クラス変数などのいくつかの追加情報とともに、すべて1つの見やすいファイルで実装へのインターフェイスの形式を提供するため、常に気に入っていました。

クラスごとに2つのファイルを使用して記述された多くのC#コード(クラスごとに2つのファイルを必要としない)が表示されます。1つは実際のクラス実装で、もう1つはインターフェイスです。この設計は、モッキング(一部のシステムでは必須)に適していて、IDEを使用してコンパイル済みメタデータを表示することなく、クラスのドキュメントを定義するのに役立ちます。私はこれまでのところ、その良い習慣を言うつもりです。

したがって、C / C ++がヘッダーファイルで同等の(ある種の)インターフェイスを強制するのは良いことです。

「2つのファイルに物を入れる必要があるとコードをハックするのが難しい」などの理由でそれらを好まない他のシステムの支持者がいることは知っていますが、私の態度はコードをハックするだけでは良い習慣ではないということです、もう少し考えてコードの作成/設計を開始すると、当然のことながらヘッダー/インターフェイスを定義することになります。


異なるファイルでのインターフェースと実装?設計上これが必要な場合は、Java / C#などの言語でインターフェイス(または抽象クラス)を1つのファイルで定義し、実装を他のファイルで非表示にするのが一般的です。これには、古いトリッキーなヘッダーファイルは必要ありません。
ペッターノールランダー

え?それは私が言ったことではありません-あなたは2つのファイルでインターフェイスとクラス実装を定義します。
gbjbaanb

2
まあ、それはヘッダーファイルを必要とせず、ヘッダーファイルをそれ以上良くしません。はい、レガシーの言語では必要ですが、概念として、インターフェース/実装を異なるファイルに分割する必要はありません(実際、悪化させます)。
ペッターノールランダー

@Petter私はあなたが誤解していると思います、概念的には違いはありません-ヘッダーはC言語のインターフェースを提供するために存在し、あなたは正しいですが-あなたは2を組み合わせることができます私が見たベストプラクティスは多くの場所で続きました。たとえば、私が見たすべてのC#開発者は、インターフェイスをクラスから分離しました。これを行うことをお勧めします。この場合、インターフェイスファイルを汚染なしに他の複数のファイルに含めることができます。さて、この分割がベストプラクティスであると考えている場合(実際)、Cで分割する方法はヘッダーファイルを使用することです。
gbjbaanb 14年

インターフェイスと実装を分離するためにヘッダーが実際に有用である場合、プライベートコンポーネントを非表示にしてエンドユーザーをそれらに対して再コンパイルする必要をなくすためにPImplのようなものは必要ありません。代わりに、私たちは両方の世界の最悪を取得します。ヘッダーは、すぐに却下された分離の外観を装い、無数の落とし穴を開きます。それらを回避する必要がある場合、冗長な定型コードが必要です。
underscore_d

1

実際、ヘッダーファイルは、インターフェイスと実装が濁っているため、あまり良くありません。プログラミング全般、特にOOPの目的は、定義されたインターフェイスを持ち、実装の詳細を隠すことですが、C ++ヘッダーファイルには、メソッド、継承、パブリックメンバー(インターフェイス)、プライベートメソッドおよびプライベートメンバーの両方が表示されます(実装の一部)。言うまでもなく、場合によってはヘッダーファイルにコードまたはコンストラクターをインライン化し、一部のライブラリにはヘッダーにテンプレートコードが含まれます。これにより、実装とインターフェイスが実際に混在します。

元々の意図は、スクリプトの内容全体をインポートすることなく、コードが他のライブラリ、オブジェクトなどを使用できるようにすることだったと思います。必要なのは、コンパイルしてリンクするヘッダーだけです。そのようにして時間とサイクルを節約します。その場合、それはまともなアイデアですが、これらの問題を解決する方法の1つにすぎません。

プログラムの構造の閲覧に関しては、ほとんどのIDEがその機能を提供しており、インターフェイスをキックアウトしたり、コード分析、逆コンパイルなどを行ったりする多くのツールがあります。

他の言語が同じ機能を実装しないのはなぜですか?なぜなら、他の言語は他の人から来ており、それらのデザイナー/クリエーターは物事がどのように機能するべきかについて異なるビジョンを持っているからです。

最良の答えは、あなたがしなければならない仕事を何に固執し、あなたを幸せにすることです。


0

多くのプログラミング言語では、プログラムが複数のコンパイル単位に細分化されると、コンパイラがそれらの内容を知る手段を持っている場合、あるユニットのコードは別のユニットで定義されたもののみを使用できます。1つのコンパイル単位を処理するコンパイラーが、現在のユニット内で使用されるものを定義するすべてのコンパイル単位のテキスト全体を検査することを要求するのではなく、コンパイラーは、各ユニットの処理中に、コンパイルされるユニットの全文を受け取るのが最善です他のすべてからの少しの情報で。

Cでは、プログラマーは各ユニットに対して2つのファイルを作成する責任があります。1つはそのユニットのコンパイル時にのみ必要な情報を含み、もう1つは他のユニットのコンパイル時に必要な情報を含みます。これはやや厄介でハックの多い設計ですが、コンパイラが中間ファイルを生成および処理する方法について言語標準で指定する必要がなくなります。他の多くの言語は異なるアプローチを使用します。コンパイラは、各ソースファイルから、外部コードからアクセスできるはずのソースファイル内のすべてを記述する中間ファイルを生成します。このアプローチにより、2つのソースファイルに重複した情報を含める必要がなくなりますが、コンパイルプラットフォームでCに不要な方法でファイルのセマンティクスを定義する必要があります。

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