C ++は、共通の共通の祖先を持つ複数の継承をどのように処理しますか?


13

私はC ++の男ではありませんが、これについて考えることを余儀なくされています。C ++では多重継承が可能ですが、C#ではできないのはなぜですか?(ダイヤモンドの問題を知っていますが、それは私がここで尋ねていることではありません)。C ++は、複数の基本クラスから継承された同一のメソッドシグネチャのあいまいさをどのように解決しますか?そして、なぜ同じデザインがC#に組み込まれていないのですか?


3
完全を期すために、「ダイヤモンドの問題」とは何ですか?
jcolebrand

1
@jcolebrand en.wikipedia.org/wiki/Multiple_inheritanceダイアモンドの問題
-l46kok

1
確かに、私はそれをグーグルで検索しましたが、それがSandeepの意味であることをどうやって知るのですか?あいまいな名前で何かを参照する場合、「共有された共通の祖先との複数の継承」がより直接的な場合
...-jcolebrand

1
@jcolebrand質問から得たものを反映するように編集しました。文脈からウィキペディアで参照されているダイヤモンドの問題を意味していると思います。次回はフレンドリーなコメントをドロップし、光沢のある新しい提案された編集ツールを使用してください:)
Earlz

1
参照を理解している場合にのみ機能する@Earlz。それ以外の場合、私は悪い編集を行っており、それはただ悪いジュジュです。
jcolebrand

回答:


24

C ++では多重継承が可能ですが、C#ではできないのはなぜですか?

Javaでは、言語の表現力を制限して言語を習得しやすくしたいと考えています。なぜなら、多重継承を使用するコードは、多くの場合、それ自体の利益よりも複雑すぎるからです。また、完全な多重継承の実装ははるかに複雑であるため、仮想マシンも大幅に簡素化されました(多重継承は、オブジェクトの中央(ベースの先頭)にポインターを保持する必要があるため、ガベージコレクターと特に相互作用します) )

そして、C#を設計するとき、Javaを見て、完全な多重継承が実際にあまり見逃されていないことを見て、物事を単純にすることを選択したと思います。

C ++は、複数の基本クラスから継承された同一のメソッドシグネチャのあいまいさをどのように解決しますか?

しませ。特定のベースから明示的にベースクラスメソッドを呼び出す構文がありますが、仮想メソッドの1つだけをオーバーライドする方法はありません。サブクラスでメソッドをオーバーライドしない場合、ベースを指定せずに呼び出すことはできません。クラス。

そして、なぜ同じデザインがC#に組み込まれていないのですか?

組み込むものは何もありません。


Giorgioがコメントでインターフェイス拡張メソッドに言及しているので、ミックスインとは何か、それらがさまざまな言語でどのように実装されているかを説明します。

JavaおよびC#のインターフェイスは、メソッドの宣言のみに制限されています。ただし、インターフェイスを継承する各クラスにメソッドを実装する必要があります。ただし、一部のメソッドのデフォルトの実装を他のメソッドの観点から提供すると便利なインターフェイスの大きなクラスがあります。一般的な例は同等です(擬似言語):

mixin IComparable {
    public bool operator<(IComparable r) = 0;
    public bool operator>(IComparable r) { return r < this; }
    public bool operator<=(IComparable r) { return !(r < this); }
    public bool operator>=(IComparable r) { return !(r > this); }
    public bool operator==(IComparable r) { return !(r < this) && !(r > this); }
    public bool operator!=(IComparable r) { return r < this || r > this; }
};

完全なクラスとの違いは、これにデータメンバーを含めることができないことです。これを実装するにはいくつかのオプションがあります。明らかに、多重継承は1つです。ただし、多重継承の実装はかなり複雑です。ただし、ここでは必要ありません。代わりに、多くの言語では、クラス自体に挿入されるか、中間ベースクラスが生成されてそこに配置される、クラスとメソッド実装のリポジトリによって実装されるインターフェイスでミックスインを分割することでこれを実装します。これはRubyとDで実装され、Java 8で実装され、不思議な繰り返しテンプレートパターンを使用してC ++で手動で実装できます。上記のCRTP形式では、次のようになります。

template <typename Derived>
class IComparable {
    const Derived &_d() const { return static_cast<const Derived &>(*this); }
public:
    bool operator>(const IComparable &r) const { r._d() < _d(); }
    bool operator<=(const IComparable &r) const { !(r._d() < _d(); }
    ...
};

そして次のように使用されます:

class Concrete : public IComparable<Concrete> { ... };

これは、通常の基本クラスのように仮想として宣言する必要はありません。そのため、テンプレートでインターフェイスを使用する場合、有用な最適化オプションを開いたままにします。C ++では、これはおそらく2番目の親として継承されますが、複数の継承を許可しない言語では、単一の継承チェーンに挿入されることに注意してください。

template <typename Derived, typename Base>
class IComparable : public Base { ... };
class Concrete : public IComparable<Concrete, Base> { ... };

コンパイラの実装は、仮想ディスパッチを回避する場合と回避しない場合があります。

C#では別の実装が選択されました。C#では、実装は完全に独立したクラスの静的メソッドであり、指定された名前のメソッドは存在しないが、「拡張メソッド」が定義されている場合、メソッド呼び出し構文はコンパイラによって適切に解釈されます。これには、すでにコンパイルされたクラスに拡張メソッドを追加できるという利点と、最適化されたバージョンを提供するなど、そのようなメソッドをオーバーライドできないという欠点があります。


インターフェース拡張メソッドを使用して、Java 8に多重継承が導入されることに言及したい場合があります。
ジョルジオ

@Giorgio:いいえ、Javaでは多重継承は絶対に導入されません。ミックスインは非常に異なるものですが、多重継承を使用する多くの残りの理由と、不思議な繰り返しテンプレートパターン(CRTP)を使用するほとんどの理由をカバーし、ほとんど多重継承とは異なり、CRTPのように動作します。
-Jan Hudec

多重継承では、オブジェクトの中央へのポインターは必要ない思います。もしそうなら、複数のインターフェイスの継承もそれを必要とするでしょう。
svick

@svick:いいえ、ありません。しかし、メンバーアクセスに仮想ディスパッチを必要とするため、この代替手段の効率ははるかに低くなります。
Jan Hudec

私よりもはるかに完全な答えを得るために+1。
ネイサンC.トレッシュ

2

答えは、名前空間の衝突が発生した場合、C ++では正しく機能しないということです。参照してくださいこれを。名前空間の衝突を避けるために、ポインタを使用してあらゆる種類の回転を行う必要があります。私はVisual StudioチームのMSで働いていましたが、少なくとも一部は、名前空間の衝突を完全に回避するために委任を開発しました。以前、インターフェイスも多重継承ソリューションの一部であると考えていたと言っていましたが、私は間違っていました。インターフェイスは実に驚くべきものであり、C ++、FWIWで動作するように作成できます。

委任は、特に名前空間の衝突に対処します。5つのクラスに委任できます。5つのクラスすべてが、メソッドをファーストクラスメンバーとしてスコープにエクスポートします。これを見ると外側は多重継承です。


1
これがMIをC#に含めない主な理由であるとは考えにくい。さらに、この投稿では、「名前空間の衝突」がない場合にMIがC ++で動作する理由についての質問には答えていません。
Doc Brown

@DocBrown私はMSでVisual Studioチームに勤務しましたが、名前空間の衝突を完全に回避するために、少なくとも一部は委任とインターフェイスを開発した理由であることを保証します。質問の質については、まあ。あなたのダウン投票を使用して、他の人はそれが有用であると考えるようです。
ネイサンC.トレッシュ

1
私はそれが正しいものだと思うので、あなたの答えを支持するつもりはありません。しかし、あなたの答えは、MIがC ++では完全に使用できないように見せかけているため、C#ではMIを導入しなかったのです。私はC ++でMIをあまり好きではありませんが、完全に使いにくいとは思いません。
Doc Brown

1
それが理由の1つであり、機能しないことを意味するつもりはありませんでした。コンピューティングで何かが非決定的であるとき、私はそれが「壊れている」と言う傾向がある、または「ブードゥー教のようだ、機能するかもしれない、機能しない、ろうそくに火をつけて祈る」ことを意味するとき、それは「機能しない」と言う傾向がある。:D
ネイサンC.トレッシュ

1
「名前空間の衝突」は非決定的ですか?
ドックブラウン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.