web-dev-qa-db-ja.com

C ++とJava

Herb Sutterによると、実装を可能な限り分離するために、C++の抽象クラスよりも抽象インターフェース(すべての純粋仮想関数)を優先する必要があります。私は個人的にこのルールが非常に便利であると感じていますが、最近多くのJavaプログラマーがいるチームに参加しました。Javaコードにはこのガイドラインが存在しないようです。関数とその実装は、抽象クラスに非常に頻繁に配置されます。C++でもHerb Sutterを誤解したのでしょうか、それとも、Javaと比較してC++での抽象関数の使用法に一般的な違いがあります。実装コードを持つ抽象クラスは、 Java C++よりも多く、もしそうならなぜですか?

13
Martin

OOPには構成と置換があります。

C++には、複数の継承、テンプレートの特殊化、埋め込み、および値/移動/ポインターのセマンティクスがあります。

Javaには単一の継承とインターフェースがあり、埋め込みと参照のセマンティクスがあります。

OOP=学校がこれらの言語を使用する一般的な方法は、オブジェクトの置換と組込みのための埋め込みに継承を採用することです。しかし、共通の祖先とランタイムキャストする方法も必要です(C++ではdynamic_cast、in Javaは別のインターフェースに要求しているだけです)。

Javaはこのすべてを独自のJava.lang.Object根付き階層。 C++には事前定義された共通ルートがないため、同じ「画像」になるように少なくとも定義する必要があります(ただし、これは一部のC++の可能性を制限しています...)。

その後、コンパイル時のポリモーフィズム(CRTPと考えてください)と値のセマンティクスを持つ可能性は、「OOPオブジェクト」の概念をC++プログラムに移植する方法に他の選択肢を提供することもできます。

異端者が埋め込みと暗黙の変換を使用して置換を管理し、プライベート継承を使用して構成を管理し、実際に伝統的な学校のパラダイムを逆転させることも想像できます。 (もちろん、この方法は他の方法よりも20歳年下であるため、それを行うことで幅広いコミュニティのサポートを期待しないでください)

または、すべてのクラスの仮想共通ベースを想像し、最終的なクラス(完全に実装された)へのインターフェース(実装なし)を形成し、部分的に実装されたインターフェースまたは偶数のインターフェースクラスターを通過し、「支配」を「マルチスタックを通じてインターフェースから実装へのディスパッチとして使用する」 -parallelogram」継承スキーム。

OOP to Java to C++ to one one and only only OOP=と仮定すると、両方の言語の機能が制限されます。

JavaコーディングイディオムをC++に厳密に準拠させると、C++が変性します。JavaをC++のような言語として動作させると、Javaが変性します。

「感度」の問題ではなく、2つの言語が持つ「集約メカニズム」の違いと、それらを組み合わせる方法が異なるため、一方の言語ではもう一方の言語よりも収益性が高くなり、逆もまた同様です。

5

原則は両方の言語に当てはまりますが、公平な比較は行っていません。 C++の純粋な抽象クラスをJavaインターフェースと比較する必要があります。

C++でも、実装されている関数のsomeを持つ抽象クラスを使用できますが、純粋な抽象クラス(実装なし)から派生させることができます。 Javaでは、同じ抽象クラス(いくつかの実装がある)があり、インターフェースから派生できる(実装なし)。

12
Luchian Grigore

一般に同じOO原則はJavaおよびC++にも当てはまります。ただし、1つの大きな違いは、C++がJava継承できるのは1つのクラスのみです。これがJavaにインターフェースがあり、多重継承の欠如を補うため、そしておそらくあなたができることを制限するためです。 (多重継承の乱用について多くの批判があるため)したがって、おそらくJavaプログラマーの心には、抽象クラス間により強い区別があるおよびインターフェース。抽象クラスは動作を共有および継承するために使用され、インターフェースは単に追加機能を追加するために使用されます。Javaでは、1つのクラスからのみ継承できますが、多くのインターフェースを持つことができます。ただし、C++では、純粋な抽象クラス(つまり「C++インターフェース」)areは、Javaインターフェースの目的とは異なり、動作を共有および継承するために使用されます(ただし、実装に必要したがって、使用方法はJavaインターフェースとは異なります。

4
Jesse Good

場合によっては、デフォルトの実装をいくつか持つことが理にかなっています。たとえば、すべてのサブクラスに適用できる汎用のPrintError(string msg)メソッド。

virtual PrintError(string msg) { cout << msg; }

本当に必要な場合でもオーバーライドできますが、ジェネリックバージョンの呼び出しだけを許可することで、クライアントの手間を省くことができます。

0
Science_Fiction