私はC++の人ではありませんが、これについて考えざるを得ません。 C#ではできないがC++では多重継承が可能なのはなぜですか? (私は ダイヤモンドの問題 を知っていますが、それは私がここで尋ねていることではありません)。 C++は、複数の基本クラスから継承された同一のメソッドシグネチャのあいまいさをどのように解決しますか?また、同じデザインがC#に組み込まれていないのはなぜですか。
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#では、実装は完全に別のクラスの静的メソッドであり、指定された名前のメソッドが存在せず、「拡張メソッド」が定義されている場合、メソッド呼び出し構文はコンパイラーによって適切に解釈されます。これには、すでにコンパイルされたクラスに拡張メソッドを追加できるという利点と、そのようなメソッドをオーバーライドできないという欠点があります。最適化されたバージョンを提供します。
答えは、名前空間の衝突が発生した場合、C++では正しく機能しないことです。 this を参照してください。名前空間の衝突を回避するには、ポインタを使ってあらゆる種類の旋回を行わなければなりません。私はVisual StudioチームのMSで働いていましたが、少なくとも一部には、名前空間の衝突を完全に回避するために委任を開発した理由があります。以前は、インターフェイスも多重継承ソリューションの一部であると考えていたと述べていましたが、私は誤っていました。インターフェイスは実際には驚くべきものであり、C++、FWIWで動作するように作成できます。
委任は特に名前空間の衝突に対処します。5つのクラスに委任することができ、5つすべてのクラスのメソッドが最初のクラスメンバーとしてスコープにエクスポートされます。これを外側で見るIS多重継承。