私は宿題をしていて、GRASPの「保護されたバリエーション」に従って、どちらのアプローチがより良いかを評価する必要があります。Stack Overflowで、C ++でのヘッダーファイルとコードファイルの分離に関する質問を見つけました。
ただし、Javaがクラス定義とクラス実装の分離を促進する際にC ++に従わない理由を知りたいのですが。C ++メソッドに比べて、Javaメソッドには利点がありますか?
private
ため、実装はサイズとprivate
メンバー関数も認識します。
私は宿題をしていて、GRASPの「保護されたバリエーション」に従って、どちらのアプローチがより良いかを評価する必要があります。Stack Overflowで、C ++でのヘッダーファイルとコードファイルの分離に関する質問を見つけました。
ただし、Javaがクラス定義とクラス実装の分離を促進する際にC ++に従わない理由を知りたいのですが。C ++メソッドに比べて、Javaメソッドには利点がありますか?
private
ため、実装はサイズとprivate
メンバー関数も認識します。
回答:
次のプログラムには何行のコードがありますか?
#include <iostream>
int main()
{
std::cout << "Hello, world!\n";
return 0;
}
おそらく7と答えます(空白行を数えなかった場合は6、中かっこを数えなかった場合は4)。
ただし、コンパイラは非常に異なるものを認識します。
~$ cpp hello.cpp | wc
18736 40822 437015
はい、これは「Hello、world!」の18.7 KLOCです。プログラム。C ++コンパイラはそれらすべてを解析する必要があります。これが、C ++のコンパイルに他の言語に比べて時間がかかる主な理由であり、現代の言語がヘッダーファイルを避けている主な理由です。
より良い質問は
C ++はCのスーパーセットとして設計されたため、下位互換性のためにヘッダーファイルを保持する必要がありました。
その原始的な別々のコンパイルモデルのために。Cコンパイラによって生成されたオブジェクトファイルには型情報が含まれていないため、型エラーを防ぐために、ソースコードにこの情報を含める必要があります。
~$ cat sqrtdemo.c
int main(void)
{
/* implicit declaration int sqrt(int) */
double sqrt2 = sqrt(2);
printf("%f\n", sqrt2);
return 0;
}
~$ gcc -Wall -ansi -lm -Dsqrt= sqrtdemo.c
sqrtdemo.c: In function ‘main’:
sqrtdemo.c:5:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
sqrtdemo.c:5:5: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
~$ ./a.out
2.000000
適切な型宣言を追加すると、バグが修正されます。
~$ cat sqrtdemo.c
#undef printf
#undef sqrt
int printf(const char*, ...);
double sqrt(double);
int main(void)
{
double sqrt2 = sqrt(2);
printf("%f\n", sqrt2);
return 0;
}
~$ gcc -Wall -ansi -lm sqrtdemo.c
~$ ./a.out
1.414214
がないことに注意してください#include
。しかし、多くの外部関数(ほとんどのプログラムで使用されます)を使用する場合、それらを手動で宣言するのは面倒でエラーが発生しやすくなります。ヘッダーファイルを使用する方がはるかに簡単です。
タイプ情報を含む別のオブジェクトファイル形式を使用する。たとえば、Java * .classファイル形式には、フィールドとメソッドパラメータのタイプを指定する「記述子」が含まれています。
これは新しい発明ではありませんでした。以前(1987年)、Borlandが個別にコンパイルされた「ユニット」をTurbo Pascal 4.0に追加したとき、ヘッダーファイルの必要性をなくすために*.TPU
、Turbo Cではなく新しいフォーマットを使用することを選択しました*.OBJ
。
OBJ
ではなく出力ファイルに設定できると確信していますTPU
...
Javaには、コントラクトを定義するためのインターフェースがあります。これにより、呼び出し元が必要とするものと実際の実装からより高いレベルの抽象化が得られます。つまり、呼び出し元は実装クラスを知る必要はなく、サポートするコントラクトを知るだけで済みます。
Mapのすべてのキー/値を遅くするメソッドを書きたいとしましょう。
public static <K,V> void printMap(Map<K,V> map) {
for(Entry<K,V> entry: map.entrySet())
System.out.println(entry);
}
このメソッドは、それを実装するクラスから削除された抽象インターフェースでentrySet()を呼び出すことができます。このメソッドを呼び出すことができます。
printMap(new TreeMap());
printMap(new LinkedHashMap());
printMap(new ConcurrentHashMap());
printMap(new ConcurrentSkipListMap());
#define interface class
。
virtual
がポリモーフィズムを取得するためにキーワードを使用する必要がなくなることを知りませんでした。Javaのように1つまたは2つの具象型を使用するだけの場合、これはパフォーマンスを低下させません。これがC ++でどのように機能するかについてのドキュメントを教えてもらえますか?
ヘッダーは、個別のコンパイルを可能にするためにあります。ヘッダーを#includeすることにより、コンパイラーはコンパイルされたC ++コードのバイナリー構造について何も知る必要がなく、その仕事を別のリンカーに任せることができます。Javaはそのコンパイラーで別個のリンカーを使用せず、.classファイルは厳密に定義されているため、コンパイラーはそれらを読み取って、すべてのメンバーをすべてのタイプで判別でき、各コンパイル単位でそれらを再宣言する必要はありません。
C ++ヘッダーにすべての実装を含めることができますが、#includeが実行されるたびにコンパイラーがそれを再コンパイルするため、リンカーは重複するコピーを整理して破棄する必要があります。
Javaはクラスの定義と実装の分離を促進しますが、それはあなたがどこから見ているかに依存します。
Javaクラスの作成者は、クラスの定義とその実装を1つのファイルで確認できます。これにより、クラスを維持するために1つの場所に行くだけでよく、2つのファイル(C ++のように.hと.cpp)を切り替える必要がないので、開発プロセスが簡素化されます。ただし、クラスのコンシューマーである場合は、.jarまたはスタンドアロンの.classにパッケージ化されている.classファイルを介して、定義のみを扱います。
C ++では、定義と実装を分離できますが、アドホックです。たとえば、ヘッダーファイル内にメソッドをインラインで記述するのを止めることは何もありません。テンプレートクラスの場合、これは必須です。ヘッダーファイルには、メンバー変数もリストされます。メンバー変数は、クラスの実装の詳細であり、コンシューマーとは関係ありませんが、ヘッダーファイルを見ると誰でも見ることができます。