C++ ではヘッダー ファイルを削除する必要がありますか?
-
09-09-2019 - |
質問
Java、C# などの多くの言語では、宣言と実装が分離されていません。C# には部分クラスの概念がありますが、実装と宣言は同じファイル内に残ります。
なぜ C++ には同じモデルがないのですか?ヘッダー ファイルを使用する方が実用的でしょうか?
ここで言及しているのは、C++ 標準の現在および今後のバージョンです。
解決
私は定期的にC#とC ++の間で反転、およびC#でヘッダファイルの欠如は、私の最大のいらいらの一つです。私は、ヘッダファイルを見て、私はクラスを知るために必要なすべてを学ぶことができます - それは、メンバ関数が呼び出されるなど、その呼び出し構文、何を - 。クラスを実装するコードのページを苦労することなく、
そして、はい、私は部分クラスと#regionsについて知っているが、それは同じではありません。クラス定義を複数のファイルにまたがっているので、部分的なクラスは、実際に、問題を悪化させます。限り#regionsが行くように、彼らは私が現時点ではやっているもののために希望の方法で展開されているように見えることはありませんので、私はそれらのほとんどのプラスの私は、ビューを取得するまで権利を拡大する時間を費やす必要があります。
Visual StudioのインテリセンスはC ++のためのよりよい働いていた場合、おそらく、私はそう頻繁に.hファイルを参照する必要があるために説得力のある理由を持っていないだろうが、でもVS2008で、C ++の
のインテリセンスはC#に触れることができません "他のヒント
下位互換性 ヘッダのファイルは消えないので休憩後退に対応しています。
ヘッダ・ファイルには、独立したコンパイルが可能になります。あなたは、ファイルをコンパイルするためにアクセスしたり、実装ファイルを用意する必要はありません。これは、分散ビルドより簡単のために作ることができます。
これはまたのSDKが少し楽に行うことができるようになります。あなたは、ヘッダーといくつかのライブラリを提供することができます。他の言語を使用してこの回避する方法は、もちろん、あります。
でもビャーネ・ストロヴストルップのヘッダーはその場しのぎのファイルと呼ばれています。
しかし、必要なメタデータを含んでいる標準のバイナリ形式なし(Javaクラスファイル、または.NET PEファイルのように)私は、機能を実装するためにどのような方法が表示されません。取り除かELFやa.outのバイナリは、あなたが抽出するために必要となる情報の多くを持っていません。そして、私は情報が今までのWindows XCOFFファイルに格納されていることはないと思う。
のの設計と進化をrel="noreferrer">、Stroustrup氏は、もう一つの理由を与えます...
同じヘッダファイルを同時に後処理時ことができる複数のプログラマによってソース制御システムを必要とせずに二つ以上の実装ファイルを持つことができます。
このは、これらの日奇妙に思えるかもしれないが、私はC ++が発明された重要な問題だったと思います。
C はコンパイラを簡単に書くために作られました。その 1 つの原則に基づいて多くのことを実行します。ポインターは、ヘッダー ファイルと同様、コンパイラーの作成を容易にするためにのみ存在します。C++ に引き継がれる機能の多くは、コンパイラの作成を容易にするために実装されたこれらの機能との互換性に基づいています。
実際、それは良いアイデアです。C が作成されたとき、C と Unix は一種のペアでした。C は Unix を移植し、Unix は C を実行しました。このようにして、C と Unix はプラットフォームからプラットフォームへと急速に普及することができましたが、アセンブリベースの OS を移植するには完全に書き直す必要がありました。
あるファイルでインターフェイスを指定し、別のファイルで実装を指定するという概念はまったく悪い考えではありませんが、C ヘッダー ファイルはそうではありません。これらは、コンパイラーがソース コードを通じて実行する必要があるパスの数を制限し、ファイル間の通信を可能にする限定的な抽象化を可能にする単なる方法です。
これらのアイテム、ポインター、ヘッダー ファイルなど...他のシステムに比べて何の利点もありません。コンパイラにさらに力を入れることで、まったく同じオブジェクト コードへのポインタと同じくらい簡単に参照オブジェクトをコンパイルできます。これが現在 C++ で行われていることです。
C は優れたシンプルな言語です。機能セットは非常に限られており、それほど労力をかけずにコンパイラを作成できました。移植は一般に簡単です。私はそれが悪い言語だとか何か言いたいわけではありません。ただ、C が作成されたときの主な目的は、現在では多かれ少なかれ不必要であるものの、互換性のために残される予定の残骸を言語内に残す可能性があるということです。
C が Unix を移植するために書かれたとは信じていない人もいるようです。(から)
UNIXの最初のバージョンはアセンブラー言語で書かれていましたが、トンプソンの意図は、それが高レベルの言語で書かれているということでした。
トンプソンは1971年に最初にPDP-7でFortranを使用しようと試みましたが、初日をgaveめました。それから彼はBと呼ばれる非常にシンプルな言語を書き、それをPDP-7に掲載しました。うまくいきましたが、問題がありました。第一に、実装が解釈されたため、常に遅くなるでしょう。第二に、単語指向のBCPLに基づいたBの基本的な概念は、新しいPDP-11のようなバイト指向のマシンにとっては正しくありませんでした。
リッチーはPDP-11を使用してbにタイプを追加しました。これはしばらくの間、「新しいB」のNBと呼ばれ、その後コンパイラを書き始めました。「そのため、Cの最初のフェーズは、実際にはこれら2つのフェーズであり、最初に、bからのいくつかの言語の変化の短い連続で、実際には、構文をあまり変化させることなくタイプ構造を追加しました。そして、コンパイラをやっています」とリッチーは言いました。
「第2段階は遅かった」と彼はCでUnixを書き直したと語った。トンプソンは1972年の夏に始まりましたが、2つの問題がありました。基本的な共同ルーチンの実行方法、つまり、あるプロセスから別のプロセスに制御を切り替える方法を見つけます。Cの元のバージョンには構造がないため、適切なデータ構造を取得することが困難です。
「物事の組み合わせにより、ケンは夏の間あきらめました」とリッチーは言いました。「1年にわたって、私は構造を追加し、おそらくコンパイラコードをやや良くしました - より良いコード - そして、次の夏に、それが協調した努力をし、実際にCでオペレーティングシステム全体をやり直しました。」
これが私が言いたいことの完璧な例です。コメントから:
ポインタはコンパイラの作成を容易にするためにのみ存在するのでしょうか?いいえ。ポインタが存在するのは、ポインタが間接化の概念を可能な限り単純に抽象化したものであるためです。– アダム・ローゼンフィールド (1時間前)
あなたが正しいです。間接化を実装するには、ポインタを実装するのが最も簡単な抽象化です。決して、理解したり使用したりするのが最も簡単なわけではありません。配列ははるかに簡単です。
問題?ポインターと同じくらい効率的に配列を実装するには、膨大な量のコードをコンパイラーに追加する必要があります。
彼らがポインターなしで次のようなコードを使用して C を設計できなかった理由はありません。
int i=0;
while(src[++i])
dest[i]=src[i];
明示的な i+src および i+dest の追加を取り除き、これと同じコードを作成するには (コンパイラ側で) 多くの労力がかかります。
while(*(dest++) = *(src++))
;
事後的に変数「i」を因数分解するのは困難です。新しいコンパイラではそれができますが、当時はそれが不可能で、そのくだらないハードウェアで実行されている OS にはそのような小さな最適化が必要でした。
現在、その種の最適化を必要とするシステムはほとんどありません (私はケーブル セットトップ ボックスという、最も遅いプラットフォームの 1 つで作業していますが、ほとんどの作業は Java で行われています)。また、まれに必要になる可能性があるのは、新しい C コンパイラです。そのような変換を自分で行うのに十分賢いはずです。
私はあなたのために良いニュースを持っています。
これはすでに存在し、D( http://www.digitalmars.com/d/呼ばれindex.htmlをする)
技術的にはDはCより多くのよりよいように思わ++が、それはただ現時点では、多くの用途での使用に十分主流ではありません。
C ++の目標の一つは、Cのスーパーセットであることであり、それはヘッダファイルをサポートできない場合、それはそうすることは困難です。あなたは消費税のヘッダファイルをご希望の場合や、拡張機能によって、あなたにも完全にCPP(プリプロセッサではなく、プラスプラス)を切除検討することができます。 C#とJavaの両方が彼らの標準とマクロプリプロセッサを指定しない(それは、彼らがすることができさえも、これらの言語で使用されているいくつかのケースで注意しなければならない)。
C ++が今に設計されているように、あなたはプロトタイプを必要とする - ちょうどCのように - 静的外部関数やクラスを参照するすべてのコンパイルされたコードを確認すること。ヘッダファイルがないと、あなたはそれらを使用する前にこれらのクラス定義と関数宣言を入力する必要があります。 C ++のヘッダファイルを使用しないためには、Javaのimport
キーワードのようなものをサポートする言語の機能を追加する必要があると思います。それは、主要な付加であり、変更したいです。それが実用的だろう場合のあなたの質問に答えるために: - すべてではない。
多くの人々は、ヘッダファイルの欠点を認識しており、C ++へのより強力なモジュールシステムを導入するアイデアがあります。 あなたはCでモジュールを見てみたいことがあります++ Daveed Vandevoordeによってに(改訂5)。
さて、C ++自体はので後方互換性のヘッダファイルを排除するべきではありません。しかし、私は彼らが一般的で愚かなアイデアだと思います。あなたはクローズドソースのlibを配布したい場合は、この情報を自動的に抽出することができます。あなたは、W / Oの実装を見てクラスを使用する方法を理解したい場合は、それがドキュメンテーションジェネレータがためであり、彼らは多くのより良い仕事の一体を行うものです。
実装ファイルに別個の構成要素でクラスインターフェースを定義する値です。
これは、インターフェースで行うことができますが、その道を行けば、あなたは暗黙的にクラスが契約から実装を分離するという点では不十分であると言っている。
のModula 2は正しい考え、定義モジュールと実装モジュールを持っていました。 http://www.modula2.org/reference/modules.phpする
のJava / C#の答えは(オブジェクト指向にもかかわらず。)同一の暗黙的な実装である
ヘッダファイルは、(例えばプライベート変数として)実装の詳細を表現するためヘッダ・ファイルは、その場しのぎであります
JavaやC#に上を移動するには、私は言語は(パブリッククラスのインターフェイスは、クラスのブラウザで航行しているように)開発のためのIDEサポートを必要とする場合、これは多分コードがその上に立っていないことを声明であることがわかり特に読めるものとして自分の長所ます。
私はかなり恐ろしい実装の詳細とのインタフェースのミックスを見つけます。
重要なことは、実装の簡潔な、よくコメントしたファイルの独立でパブリッククラス署名を文書化する能力の欠如は、言語設計は、メンテナンスの利便むしろ、原作者の便宜のために書かれていることを私に示しています。さて、私は今、JavaとC#についてとりとめています。
今回の分離のための簡単なビューのインタフェース なし 必要 高度な編集.
あなたは、これは決して起こらない理由たい場合は:それはほとんどすべての既存のC ++ソフトウェアを破るを。あなたがC ++委員会の設計ドキュメントのいくつかを見れば、彼らはそれを破るだろうどのくらいのコードを参照するために、様々な代替案を見ます。
途中インテリジェント何かにswitch文を変更することがはるかに容易になるだろう。それは少しだけコードを破ります。まだ起こることはないだろう。
NEW IDEAのために編集:
C ++ヘッダ、必要なファイルになりC ++とJavaとの間の差は、C ++オブジェクトは必ずしもポインタではないということです。それはそのようには見えませんが、Javaでは、すべてのクラスのインスタンスは、ポインタによって参照されています。 C ++は、ヒープとスタック上に割り当てられたオブジェクトを持っています。これは、オブジェクトがものになるかどうかの大きな知る方法を必要とし、データメンバがメモリ内にあるC ++を意味します。
いいえ言語は、ヘッダファイルなしで存在しません。それは神話です。
(私が話をする何のC#の経験を持っていないが、私はそれは同じだ期待する)は、Javaのための任意の独自のライブラリの分布を見てください。彼らはあなたに完全なソースファイルを与えることはありません。彼らはすべてのメソッドの実装をあなたにファイルを与える({}
または{return null;}
など)と、彼らは隠し隠して逃げることができ、すべてをブランク。あなたは、ヘッダーが、その何かを呼び出すことはできません。
技術的な理由はありません、しかし、そのファイルを直接コンパイルされていない限り、CまたはC ++コンパイラはextern
として適切にマークされたファイルのすべてを数えることができる理由。 CやC ++のどちらも解析する高速であり、そしてそれは非常に重要な考慮事項ですので、しかし、コンパイルのためのコストは計り知れないだろう。 meldingヘッダとソースのいずれかのより複雑な方法はすぐに、オブジェクトのレイアウトを知っているようにコンパイラが必要のような技術的な問題が発生するでしょう。
ヘッダ・ファイルには、言語の一部です。ヘッダファイル、すべての静的ライブラリ、動的ライブラリがなければ、ほとんどすべてのコンパイル済みのライブラリが無駄になってしまいます。ヘッダ・ファイルは、それが簡単にすべてを文書化するために行い、コードのすべての単一ビットを超えることなく、ライブラリ/ファイルのAPIに目を通すすることを可能にします。
彼らはまた、それが簡単にあなたのプログラムを整理するために作ります。はい、あなたは常にヘッダにソースからの切り替えでなければならないが、彼らはまた、あなたが実装内の内部や民間のAPIを定義することができます。たとえばます:
MySource.hます:
extern int my_library_entry_point(int api_to_use, ...);
MySource.cます:
int private_function_that_CANNOT_be_public();
int my_library_entry_point(int api_to_use, ...){
// [...] Do stuff
}
int private_function_that_CANNOT_be_public() {
}
あなたは#include <MySource.h>
場合、あなたはmy_library_entry_point
を取得します。
#include <MySource.c>
場合、あなたはをも
の取得private_function_that_CANNOT_be_public
ます。
あなたがパスワードのリスト、またはあなたの暗号化アルゴリズムを実装する機能、またはOSの内部を公開します機能、または機能を取得する機能を持っていた場合には非常に悪いものになる可能性がどのように見ますオーバーライド権限など。
はいああ!
JavaとC#でコーディングした後、それは、すべてのクラスの2つのファイルを持っていることは本当に迷惑なんです。だから私は、私は、既存のコードを壊すことなく、それらをマージすることができますどのように考えていた。
実際には、それは本当に簡単です。ただ、#ifdefのセクション内の定義(実装)を入れて、そのファイルをコンパイルするコンパイラのコマンドラインに定義を追加します。それはそれだ。
ここでは一例であります:
/* File ClassA.cpp */
#ifndef _ClassA_
#define _ClassA_
#include "ClassB.cpp"
#include "InterfaceC.cpp"
class ClassA : public InterfaceC
{
public:
ClassA(void);
virtual ~ClassA(void);
virtual void methodC();
private:
ClassB b;
};
#endif
#ifdef compiling_ClassA
ClassA::ClassA(void)
{
}
ClassA::~ClassA(void)
{
}
void ClassA::methodC()
{
}
#endif
コマンドラインで、
と、そのファイルをコンパイル-D compiling_ClassA
にClassAを含める必要があり、他のファイルだけで行うことができます。
#include "ClassA.cpp"
もちろん、コマンドライン上に定義の添加を容易にマクロ展開(Visual Studioのコンパイラ)または自動変数(GNUにする)を有すると定義名の同じ命名法を用いて追加することができます。
それでも私はいくつかのステートメントのポイントを得ることはありません。 APIと実装の分離は非常に良いことですが、ヘッダファイルには、APIではありません。そこに民間のフィールドがあります。あなたがプライベートフィールドを追加または削除した場合は、実装していないAPIを変更します。