web-dev-qa-db-ja.com

C ++に別のヘッダーファイルが必要なのはなぜですか?

C++が.cppファイルと同じ機能を持つ別個のヘッダーファイルを必要とする理由を本当に理解したことはありません。クラスの作成とリファクタリングが非常に難しくなり、プロジェクトに不要なファイルが追加されます。そして、ヘッダーファイルを含める必要があるが、既に含まれているかどうかを明示的に確認する必要があるという問題があります。

C++は1998年に批准されましたが、なぜこのように設計されているのですか?別個のヘッダーファイルを持つことにはどのような利点がありますか?


フォローアップの質問:

私がインクルードするすべてが.hファイルである場合、コンパイラはコードを含む.cppファイルをどのように見つけますか? .cppファイルは.hファイルと同じ名前を持っていると仮定しますか、それともディレクトリツリー内のすべてのファイルを実際に調べますか?

118
Marius

ヘッダーファイルには他の用途もありますが、定義を宣言から分離することを求めているようです。

答えは、C++はこれを「必要としない」ということです。すべてをインラインでマークする場合(クラス定義で定義されたメンバー関数の場合は自動)、分離する必要はありません。ヘッダーファイルですべてを定義できます。

wantを分離する理由は次のとおりです。

  1. ビルド時間を改善するため。
  2. 定義のソースを持たずにコードに対してリンクする。
  3. すべてを「インライン」にマークしないようにするため。

あなたのより一般的な質問が「なぜC++はJavaと同一ではないのですか?」である場合、「なぜJavaの代わりにC++を書いているのですか?」 ;-p

しかしもっと深刻なのは、C++コンパイラが別の変換ユニットに到達して、javacができることとする方法でそのシンボルの使用方法を見つけ出すことができないからです。ヘッダーファイルは、リンク時に利用できると予想されるものをコンパイラに宣言するために必要です。

したがって、#includeは単純なテキスト置換です。ヘッダーファイルですべてを定義すると、プリプロセッサはプロジェクト内のすべてのソースファイルの膨大なコピーと貼り付けを作成し、それをコンパイラに送ります。 C++標準が1998年に批准されたという事実はこれとは何の関係もありません。C++のコンパイル環境がCのそれに非常に密接に基づいているという事実です。

あなたのフォローアップの質問に答えるために私のコメントを変換する:

コンパイラは、コードが含まれる.cppファイルをどのように見つけますか

少なくとも、ヘッダーファイルを使用したコードをコンパイルするときはそうではありません。リンクする関数はまだ作成されていなくてもかまいません。コンパイラが.cppファイルの内容を知っていることを気にしないでください。関数宣言。リンク時に、.oファイル、または静的または動的ライブラリのリストを提供します。有効なヘッダーは、関数の定義がどこかにあることを約束します。

98
Steve Jessop

C++がそうするのは、Cがそのようにしたからです。実際の質問は、なぜCがそうしたのかということです。 Wikipedia これについて少し話しています。

新しいコンパイル言語(Java、C#など)は、前方宣言を使用しません。識別子はソースファイルから自動的に認識され、動的ライブラリシンボルから直接読み取られます。つまり、ヘッダーファイルは必要ありません。

82
Donald Byrd

一部の人々は、ヘッダーファイルを利点と考えています。

  • インターフェイスと実装の分離を有効化/強制/許可すると主張されていますが、通常はそうではありません。ヘッダーファイルは実装の詳細でいっぱいです(たとえば、クラスのメンバー変数は、パブリックインターフェイスの一部ではなくても、ヘッダーで指定する必要があります)。関数は、インラインinヘッダー内のクラス宣言。この分離も破棄します。
  • 各翻訳単位は独立して処理できるため、コンパイル時間を改善すると言われることがあります。それでも、C++は、コンパイル時間に関してはおそらく最も遅い言語です。その理由の一部は、同じヘッダーが何度も繰り返されることです。多数のヘッダーが複数の翻訳単位に含まれるため、複数回解析する必要があります。

最終的に、ヘッダーシステムは、Cが設計された70年代の成果物です。当時、コンピューターにはメモリがほとんどなく、モジュール全体をメモリに保持することは選択肢ではありませんでした。コンパイラーは、ファイルの先頭から読み取りを開始し、ソースコードを直線的に進めなければなりませんでした。ヘッダーメカニズムはこれを可能にします。コンパイラは他の翻訳単位を考慮する必要はありません。コードを上から下に読むだけです。

C++は、後方互換性のためにこのシステムを保持していました。

今日では、意味がありません。効率が悪く、エラーが発生しやすく、複雑すぎます。thatが目標だった場合、インターフェースと実装を分離するより良い方法があります。

ただし、C++ 0xの提案の1つは、適切なモジュールシステムを追加して、.NETまたはJavaと同様にコードをより大きなモジュールに、ヘッダーなしで一度にコンパイルできるようにすることでした。この提案はC++ 0xの削減にはなりませんでしたが、まだ「これを後でやりたい」というカテゴリーにあると思います。おそらく、TR2などで。

55
jalf

私の(限られた-私は通常C開発者ではない)理解するには、これはCに根ざしています。Cはクラスまたは名前空間が何であるかを知らないことを覚えておいてください。また、関数を使用する前に宣言する必要があります。

たとえば、次の例ではコンパイラエラーが発生します。

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

エラーは、「SomeOtherFunctionが宣言されていない」ということです。宣言の前に呼び出すからです。これを修正する1つの方法は、SomeOtherFunctionをSomeFunctionの上に移動することです。別のアプローチは、最初に関数シグネチャを宣言することです:

void SomeOtherFunction();

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

これにより、コンパイラーは次のことを知ることができます。コードのどこかを見てください。voidを返し、パラメーターを受け取らないSomeOtherFunctionという関数があります。したがって、SomeOtherFunctionを呼び出そうとするコードを作成する場合は、パニックに陥らず、代わりに探してください。

ここで、2つの異なる.cファイルにSomeFunctionとSomeOtherFunctionがあるとします。次に、Some.cに「SomeOther.c」を#includeする必要があります。次に、いくつかの「プライベート」関数をSomeOther.cに追加します。 Cはプライベート関数を認識していないため、その関数はSome.cでも使用できます。

これが.hファイルの出番です。他の.cファイルでアクセスできる.cファイルから「エクスポート」したいすべての関数(および変数)を指定します。そうすれば、パブリック/プライベートスコープのようなものが得られます。また、ソースコードを共有することなく、この.hファイルを他の人に渡すことができます。hファイルは、コンパイルされた.libファイルに対しても機能します。

そのため、主な理由は、ソースコードの保護と、アプリケーションの各部分の分離を少し行うためです。

しかし、それはCでした。 C++はクラスとprivate/public修飾子を導入したため、それらが必要かどうかを尋ねることはできますが、C++ AFAIKはそれらを使用する前に関数の宣言を必要とします。また、多くのC++開発者もC開発者であるか、C開発者であり、その概念と習慣をC++に引き継いでいます。なぜ壊れていないものを変更するのですか?

26
Michael Stum

最初の利点:ヘッダーファイルがない場合、他のソースファイルにソースファイルを含める必要があります。これにより、インクルードファイルが変更されると、インクルードファイルが再度コンパイルされます。

第二の利点:異なるユニット(異なる開発者、チーム、企業など)の間でコードを共有することなく、インターフェースを共有できます。

10
erelender

ヘッダーファイルの必要性は、コンパイラが他のモジュールの関数や変数の型情報について知るための制限から生じます。コンパイルされたプログラムまたはライブラリには、コンパイラが他のコンパイル単位で定義されたオブジェクトにバインドするために必要な型情報は含まれません。

この制限を補うために、CおよびC++は宣言を許可し、これらの宣言はプリプロセッサの#includeディレクティブの助けを借りてそれらを使用するモジュールに含めることができます。

JavaまたはC#のような言語には、コンパイラの出力(クラスファイルまたはアセンブリ)にバインドに必要な情報が含まれます。したがって、スタンドアロンの宣言を維持する必要はありません。モジュールのクライアントに含まれます。

バインディング情報がコンパイラーの出力に含まれない理由は簡単です。実行時に必要ない(コンパイル時に型チェックが行われる)。スペースを無駄にするだけです。 C/C++は、実行可能ファイルまたはライブラリのサイズがかなり重要であった時代からのものであることを忘れないでください。

5
VoidPointer

C++は、Cインフラストラクチャに最新のプログラミング言語機能を追加するために設計されたもので、言語自体に特化したものではないCについては不必要に変更することはありません。

はい、この時点(最初のC++標準の10年後および使用が大幅に増加し始めた20年後)に、適切なモジュールシステムがないのは簡単です。明らかに、今日設計されている新しい言語はC++のようには機能しません。しかし、それはC++のポイントではありません。

C++のポイントは、ユーザーコミュニティで適切に機能するものを壊すことなく(あまりにも頻繁に)新しい機能を追加するだけで、既存のプラクティスをスムーズに継続して進化させることです。

これは、他の言語よりも(特に新しいプロジェクトを開始する人にとって)難しいことや(特に既存のコードを維持する人にとって)難しいことを意味します。

それで、C++がC#に変わることを期待するのではなく(すでにC#を持っているので無意味です)、仕事にぴったりのツールを選んでみませんか?私自身、現代の言語でかなりの数の新しい機能を書くよう努力しています(たまたまC#を使用しています)。すべて。とにかく非常にうまく統合されているので、ほとんど痛みはありません。

4

Mainと同じ機能を持つ別個のヘッダーファイルは必要ありません。複数のコードファイルを使用してアプリケーションを開発し、以前に宣言されていない関数を使用する場合にのみ必要です。

それは本当にスコープの問題です。

3
Alex Rodrigues

さて、C++は1998年に批准されましたが、それよりもずっと長く使用されていました。批准は、主に構造を課すのではなく現在の使用法を設定することでした。また、C++はCをベースにしており、Cにはヘッダーファイルがあるため、C++にもヘッダーファイルがあります。

ヘッダーファイルの主な理由は、ファイルの個別のコンパイルを可能にし、依存関係を最小限に抑えることです。

Foo.cppがあり、bar.h/bar.cppファイルのコードを使用したいとします。

Foo.cppに「bar.h」を#includeして、bar.cppが存在しない場合でもfoo.cppをプログラムおよびコンパイルできます。ヘッダーファイルは、bar.hのクラス/関数が実行時に存在するというコンパイラーへの約束として機能し、既に知っておく必要のあるすべてのものを持っています。

もちろん、プログラムをリンクしようとしたときにbar.hの関数にボディがない場合、リンクされず、エラーが発生します。

副作用は、ソースコードを公開せずにユーザーにヘッダーファイルを提供できることです。

もう1つは、*。cppファイルのコードの実装を変更しても、ヘッダーをまったく変更しない場合は、*。cppファイルを使用するすべてではなく、*。cppファイルをコンパイルするだけです。もちろん、ヘッダーファイルに多くの実装を入れると、これはあまり役に立ちません。

3
Mark Krenitsky

コンパイラーに他のファイルで定義されたシンボルを自動的に検出させたい場合は、プログラマーにそれらのファイルを事前定義された場所(Javaパッケージ構造がプロジェクトのフォルダー構造を決定するなど)に配置するように強制する必要があります。また、使用するライブラリのソースまたはコンパイラに必要な情報をバイナリに格納するための何らかの統一された方法が必要です。

1
Tadeusz Kopec

C++は1998年に批准されましたが、なぜこのように設計されているのですか?別個のヘッダーファイルを持つことにはどのような利点がありますか?

実際にヘッダーファイルは、プログラムを初めて調べるときに非常に便利になります。ヘッダーファイルをチェックアウトすると(テキストエディターのみを使用)、プログラムのアーキテクチャの概要がわかります。クラスを表示したり、それらのメンバー関数。

1
Diaa Sami

ヘッダーファイルの背後にある本当の(歴史的な)理由は、コンパイラ開発者にとってより簡単になったと思いますが、ヘッダーファイルdoは利点をもたらします。
チェック この前の投稿 詳細については...

1
Paolo Tedesco

さて、ヘッダーファイルなしでC++を完全に開発できます。実際、テンプレートを集中的に使用するライブラリの中には、ヘッダー/コードファイルパラダイムを使用しないものがあります(boostを参照)。しかし、C/C++では、宣言されていないものは使用できません。これに対処する1つの実用的な方法は、ヘッダーファイルを使用することです。さらに、コード/実装を共有せずにインターフェイスを共有できるという利点があります。そして、私はそれがCクリエーターによって構想されていなかったと思います:共有ヘッダーファイルを使用するとき、有名なものを使用しなければなりません:

#ifndef MY_HEADER_SWEET_GUARDIAN
#define MY_HEADER_SWEET_GUARDIAN

// [...]
// my header
// [...]

#endif // MY_HEADER_SWEET_GUARDIAN

これは実際には言語機能ではなく、複数のインクルードを処理する実用的な方法です。

したがって、Cが作成されたとき、前方宣言の問題は過小評価されていたので、C++のような高レベル言語を使用するときには、この種のことを処理する必要があります。

貧しいC++ユーザーにとっての別の負担...

1
neuro