54

私は、C# に多重継承を含めることに反対する多くの議論に出くわしました。

  • 多重継承は複雑すぎてあいまいな場合が多い
  • インターフェイスは同様のものを提供するため、不要です
  • コンポジションは、インターフェイスが不適切な場合の優れた代替手段です

私は C++ のバックグラウンドを持っているので、多重継承のパワーとエレガンスが恋しいです。すべてのソフトウェア設計に適しているわけではありませんが、インターフェイス、構成、および同様の OO 手法に対する有用性を否定するのが難しい状況があります。

多重継承の除外は、開発者がそれらを賢く使用するほど賢くなく、複雑さが生じたときに対処できないと言っているのですか?

個人的には、C# (おそらく C##) への多重継承の導入を歓迎します。


補遺: 単一 (または手続き型の背景) と複数の継承の背景から来る回答から知りたいです。多重継承の経験がない開発者は、パラダイムの経験がないという理由だけで、多重継承は不要であるという議論をデフォルトで行うことがよくあります。

4

34 に答える 34

114

一度も見逃したことはありません。はい、[MI] は複雑になります。インターフェイスは多くの点で同様の仕事をしますが、それは最大のポイントではありません。一般的な意味では、ほとんどの場合必要ありません。多くの場合、単一の継承でさえ使いすぎです。

于 2008-10-10T14:50:51.977 に答える
37

継承よりも集約を優先してください!

class foo : bar, baz

多くの場合、より適切に処理されます

class foo : Ibarrable, Ibazzable
{
  ... 
  public Bar TheBar{ set }
  public Baz TheBaz{ set }

  public void BarFunction()
  {
     TheBar.doSomething();
  }
  public Thing BazFunction( object param )
  {
    return TheBaz.doSomethingComplex(param);
  }
}

このようにして、IBerrable と IBazzable のさまざまな実装をスワップインおよびスワップアウトして、さらに別のクラスを作成することなく、App の複数のバージョンを作成できます。

依存性注入は、これに大いに役立ちます。

于 2008-10-10T15:00:16.730 に答える
25

多重継承を扱う際の問題の 1 つは、インターフェイスの継承と実装の継承の違いです。

C# には、純粋なインターフェイスを使用することにより、インターフェイスの継承 (暗黙的または明示的な実装の選択を含む) のクリーンな実装が既にあります。

C++ を見ると、宣言のコロンの後に指定するクラスごとに、取得する継承の種類がアクセス修飾子 ( 、、または)classによって決まります。継承を使用すると、複数の継承の完全な混乱が発生します。複数のインターフェイスが複数の実装と混在しています。継承では、実装を取得するだけです。" " のオブジェクトは、 を期待する関数に渡されることはありません。これは、クラスが実際にはプライベートフィールドと自動的に実装される委譲パターンを持っているかのようだからです。privateprotectedpublicpublicprivateclass Foo : private BarBarFooBar

純粋な複数実装の継承 (実際には自動委譲にすぎません) には問題がなく、C# で実現できれば素晴らしいものです。

クラスからの複数のインターフェイスの継承に関しては、機能を実装するためのさまざまな設計が考えられます。多重継承を持つすべての言語には、メソッドが複数の基本クラスで同じ名前で呼び出されたときに何が起こるかについて、独自の規則があります。Common Lisp (特に CLOS オブジェクト システム) や Python などの一部の言語には、基本クラスの優先順位を指定できるメタオブジェクト プロトコルがあります。

1 つの可能性を次に示します。

abstract class Gun
{ 
    public void Shoot(object target) {} 
    public void Shoot() {}

    public abstract void Reload();

    public void Cock() { Console.Write("Gun cocked."); }
}

class Camera
{ 
    public void Shoot(object subject) {}

    public virtual void Reload() {}

    public virtual void Focus() {}
}

//this is great for taking pictures of targets!
class PhotoPistol : Gun, Camera
{ 
    public override void Reload() { Console.Write("Gun reloaded."); }

    public override void Camera.Reload() { Console.Write("Camera reloaded."); }

    public override void Focus() {}
}

var    pp      = new PhotoPistol();
Gun    gun     = pp;
Camera camera  = pp;

pp.Shoot();                    //Gun.Shoot()
pp.Reload();                   //writes "Gun reloaded"
camera.Reload();               //writes "Camera reloaded"
pp.Cock();                     //writes "Gun cocked."
camera.Cock();                 //error: Camera.Cock() not found
((PhotoPistol) camera).Cock(); //writes "Gun cocked."
camera.Shoot();                //error:  Camera.Shoot() not found
((PhotoPistol) camera).Shoot();//Gun.Shoot()
pp.Shoot(target);              //Gun.Shoot(target)
camera.Shoot(target);          //Camera.Shoot(target)

この場合、競合が発生した場合、最初にリストされたクラスの実装のみが暗黙的に継承されます。他の基本型のクラスは、それらの実装を取得するために明示的に指定する必要があります。よりばかげないようにするために、コンパイラーは、競合が発生した場合に暗黙の継承を許可しないようにすることができます (競合するメソッドには常にキャストが必要です)。

また、今日の C# では、暗黙的な変換演算子を使用して多重継承を実装できます。

public class PhotoPistol : Gun /* ,Camera */
{
    PhotoPistolCamera camera;

    public PhotoPistol() {
        camera = new PhotoPistolCamera();
    }

    public void Focus() { camera.Focus(); }

    class PhotoPistolCamera : Camera 
    { 
        public override Focus() { }
    }

    public static Camera implicit operator(PhotoPistol p) 
    { 
        return p.camera; 
    }
}

isただし、 andas演算子and でサポートされていないため、完全ではありませんSystem.Type.IsSubClassOf()

于 2008-10-10T17:36:27.133 に答える
15

これは、私が常に遭遇する多重継承の非常に便利なケースです。

ツールキット ベンダーとして、公開された API を変更することはできません。そうしないと、下位互換性が失われます。その結果として生じることの 1 つは、いったんインターフェイスをリリースすると、それを実装している人のコンパイルが中断されるため、インターフェイスに追加することはできません。唯一のオプションは、インターフェイスを拡張することです。

これは既存の顧客にとっては問題ありませんが、新しい顧客はこの階層が不必要に複雑であると見なす可能性があります。最初から設計していた場合、この方法で実装することは選択しません。そうしないと、下位互換性が失われます。 . インターフェイスが内部の場合は、それに追加して実装者を修正するだけです。

多くの場合、インターフェースへの新しいメソッドには明らかで小さなデフォルトの実装がありますが、それを提供することはできません。

私は抽象クラスを使用することを好み、メソッドを追加する必要がある場合は、デフォルトの実装で仮想クラスを追加します。これを行うこともあります。

もちろん、問題は、このクラスがすでに何かを拡張している何かに混入する可能性が高い場合です。その場合、インターフェースを使用して拡張インターフェースを処理するしかありません。

この問題を大きく抱えていると思われる場合は、代わりにリッチ イベント モデルを選択します。C# ではおそらくこれが正しい答えだと思いますが、すべての問題がこの方法で解決されるわけではありません。シンプルなパブリック インターフェイスが必要な場合もあります。 、エクステンダー向けのよりリッチなもの。

于 2008-10-10T15:34:48.753 に答える
12

C# は、単一の継承、インターフェイス、および拡張メソッドをサポートしています。それらの間で、多重継承がもたらす頭痛の種なしに、多重継承が提供するほとんどすべてを提供します。

于 2008-10-10T14:55:11.187 に答える
8

多重継承は、私が知っている方法では CLR でサポートされていないため、C++ (または Eiffel、言語が特別に設計されていることを考えると、より適切に機能する可能性があります) のように効率的な方法でサポートできるとは思えません。 MIの場合)。

Multiple Inheritance の優れた代替手段は Traits と呼ばれます。これにより、さまざまな動作単位を 1 つのクラスにまとめることができます。コンパイラは、単一継承型システムのコンパイル時の拡張機能として特性をサポートできます。クラス X にトレイト A、B、および C が含まれていることを宣言するだけで、コンパイラは要求したトレイトをまとめて X の実装を形成します。

たとえば、IList(of T) を実装しようとしているとします。IList(of T) のさまざまな実装を見ると、まったく同じコードの一部を共有していることがよくあります。共通コードを含む特性を宣言するだけで、IList(of T) の任意の実装でその共通コードを使用できます。実装に他の基本クラスが既にある場合でも同様です。構文は次のようになります。

/// This trait declares default methods of IList<T>
public trait DefaultListMethods<T> : IList<T>
{
    // Methods without bodies must be implemented by another 
    // trait or by the class
    public void Insert(int index, T item);
    public void RemoveAt(int index);
    public T this[int index] { get; set; }
    public int Count { get; }

    public int IndexOf(T item)
    {
        EqualityComparer<T> comparer = EqualityComparer<T>.Default;
        for (int i = 0; i < Count; i++)
            if (comparer.Equals(this[i], item))
                return i;
        return -1;
    }
    public void Add(T item)
    {
        Insert(Count, item);
    }
    public void Clear()
    {   // Note: the class would be allowed to override the trait 
        // with a better implementation, or select an 
        // implementation from a different trait.
        for (int i = Count - 1; i >= 0; i--)
            RemoveAt(i);
    }
    public bool Contains(T item)
    {
        return IndexOf(item) != -1;
    }
    public void CopyTo(T[] array, int arrayIndex)
    {
        foreach (T item in this)
            array[arrayIndex++] = item;
    }
    public bool IsReadOnly
    {
        get { return false; }
    }
    public bool Remove(T item)
    {
        int i = IndexOf(item);
        if (i == -1)
            return false;
        RemoveAt(i);
        return true;
    }
    System.Collections.IEnumerator 
        System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < Count; i++)
            yield return this[i];
    }
}

そして、この特性を次のように使用します。

class MyList<T> : MyBaseClass, DefaultListMethods<T>
{
    public void Insert(int index, T item) { ... }
    public void RemoveAt(int index)       { ... }
    public T this[int index] {
        get { ... }
        set { ... }
    }
    public int Count {
        get { ... }
    }
}

もちろん、ここでは表面をなぞっているだけです。より完全な説明については、論文Traits: Composable Units of Behavior (PDF) を参照してください。

Rust 言語 (Mozilla から) は興味深い方法で Traits を実装しました: 彼らは、Traits がデフォルトのインターフェース実装に似ていることに気付き、インターフェースと Traits を単一の機能 (彼らはこれを trait と呼んでいます) に統合しました。トレイトとデフォルトのインターフェイス実装 (現在 Java に実装されています) の主な違いは、パブリックでなければならない従来のインターフェイス メソッドとは異なり、トレイトにはプライベート メソッドまたは保護されたメソッドを含めることができることです。特性とインターフェースが単一の機能に統合されていない場合、もう 1 つの違いは、インターフェースへの参照を持つことはできますが、特性への参照を持つことはできないということです。特性自体は型ではありません。

于 2008-10-10T16:35:50.207 に答える
7

私は実際には、ある特定の理由で複数の継承を見逃しています.disposeパターンです。

Dispose パターンを実装する必要があるたびに、「いくつかの仮想オーバーライドを使用して Dispose パターンを実装するクラスから派生できたらいいのに」と自分に言い聞かせます。IDispose を実装するすべてのクラスに同じボイラープレート コードをコピー アンド ペーストしますが、それは嫌です。

于 2008-11-24T23:45:55.743 に答える
5

はい!はい!はい!

真剣に、私はキャリア全体で GUI ライブラリを開発してきました。MI (多重継承) は、この FAR を SI (単一継承) よりも簡単にします。

最初にC++ でSmartWin++ (MI を多用) を実行し、次に Gaia Ajax を実行し、最後にRa-Ajaxを実行しました。MI がいくつかの場所を支配していると非常に自信を持って言えます。それらの場所の1つはGUIライブラリです...

そして、MI は「複雑すぎる」などと主張する議論は、ほとんどの場合、言語戦争を構築しようとする人々によってそこに置かれ、たまたま「現在 MI を持っていない」陣営に属しています...

関数型プログラミング言語 (Lisp など) が (「非 Lispers」によって) 非関数型プログラミング言語の支持者によって「複雑すぎる」と教えられてきたのと同じように...

人は未知のものに怯える…

MIルール!

于 2008-11-25T00:38:35.887 に答える
5

あなたが述べている理由だけで、多重継承に反対します。開発者はそれを悪用します。ユーティリティ クラスから継承するすべてのクラスに関する問題を十分に見てきましたが、多くの場合、多重継承が多くの状況で悪いコードにつながることを知るために、多くのタイプを入力する必要なくすべてのクラスから関数を呼び出すことができます。GoTo についても同じことが言えます。これが、GoTo の使用が嫌われている理由の 1 つです。多重継承には、GoTo のように、いくつかの適切な用途があると思います。両方が適切な場合にのみ使用される理想的な世界では、問題はありません。しかし、世界は理想的ではないので、悪いプログラマーを自分たちから守らなければなりません。

于 2008-10-10T14:55:29.300 に答える
3

便利な場合もありますが、C#に多重継承がないことを嬉しく思います。代わりに私が見たいのは、インターフェースメソッドのデフォルトの実装を提供する機能です。あれは:

interface I
{
    void F();
    void G();
}


class DefaultI : I
{
    void F() { ... }
    void G() { ... }
}

class C : I = DefaultI
{
    public void F() { ... } // implements I.F
}

この場合、((I)new C()).F()Cの実装を呼び出し、はの実装I.F()を呼び出し((I)new C()).G()ます。DefaultII.G()

これを言語に追加する前に、言語設計者が解決しなければならない問題がいくつかありますが、どれも非常に難しい問題ではなく、結果は多重継承を望ましいものにする多くのニーズをカバーします。

于 2008-11-07T06:59:37.190 に答える
2

いいえ。

(投票用)

于 2008-10-10T15:25:48.380 に答える
2

私は、C# が最初にアルファ/ベータ リリースとして利用可能になって以来、C# を使用してきましたが、多重継承を見逃したことはありません。MI はいくつかの点で優れていますが、ほとんどの場合、同じ結果を達成する他の方法があります (そのうちのいくつかは、実際にはより単純になったり、理解しやすい実装を作成したりします)。

于 2008-10-10T14:58:53.380 に答える
2

一般に、多重継承は便利であり、多くの OO 言語は何らかの方法でそれを実装しています (C++、Eiffel、CLOS、Python...)。それは不可欠ですか?いいえ、持っていてよろしいですか?はい。

于 2008-10-10T15:01:20.847 に答える
2

Update
I challenge everyone who votes me down to show me any example of multiple inheritance that I can't easily port to a language with single inheritance. Unless anyone can show any such sample, I claim it does not exist. I have ported tons of C++ code (MH) to Java (no-MH) and that was never a problem, no matter how much MH the C++ code used.


Nobody could ever prove so far that multiple inheritance has any advantage over other techniques you mentioned in your post (using interfaces and delegates I can get exactly the same result without much code or overhead), while it has a couple of well known disadvantages (diamond problem being the most annoying ones).

Actually multiple inheritance is usually abused. If you use OO design to somehow model the real world into classes, you will never get to the point where multiple inheritance makes actually sense. Can you provide a useful example for multiple inheritance? Most of the examples I've seen so far are actually "wrong". They make something a subclass, that is in fact just an extra property and thus actually an interface.

Take a look at Sather. It is a programming language, where interfaces do have multiple inheritance, as why not (it can't create a diamond problem), however classes that are no interfaces have no inheritance whatsoever. They can only implement interfaces and they can "include" other objects, which makes these other objects a fixed part of them, but that is not the same as inheritance, it's rather a form of delegation (method calls "inherited" by including objects are in fact just forwarded to instances of these objects encapsulated in your object). I think this concept is pretty interesting and it shows you can have a complete clean OO language without any implementation inheritance at all.

于 2008-10-10T15:06:29.950 に答える
2

DataFlex 4GL v3+ の本当に素晴らしく (当時としては) 目新しかった点の 1 つは (知っている、知っている、Dataとは?)、 mixin継承のサポートでした。他のクラスのメソッドをクラスで再利用できました。これらのメソッドが使用するプロパティをクラスが提供している限り、クラスは問題なく機能し、心配する「ダイヤモンドの問題」やその他の多重継承の「落とし穴」はありませんでした。

特定の種類の抽象化と構造の問題を簡素化するため、C#でこのようなものを見たいと思います

于 2008-11-14T16:00:36.593 に答える
2

多重継承の代わりに、より優れたソリューションであるミックスインを使用できます。

于 2008-12-12T14:17:00.180 に答える
1

C# のような言語は、プログラマーに選択肢を与えるべきだと私は信じています。複雑すぎるからといって、複雑すぎるとは限りませ。プログラミング言語は、プログラマーが望むあらゆるものを構築するためのツールを開発者に提供する必要があります。

開発者が既に作成した API を使用することを選択しますが、使用していないこともあります。

于 2008-11-25T00:26:14.603 に答える
1

ダイヤモンドの問題が解決しない限り、いいえ。これが解決されない限り、コンポジションを使用できます。

于 2010-02-18T06:57:00.737 に答える
1

同僚が、動的コンパイルを使用して C# で多重継承のようなものを取得する方法について、このブログを書きました。

http://www.atalasoft.com/cs/blogs/stevehawley/archive/2008/09/29/late-binding-in-c-using-dynamic-compilation.aspx

于 2008-10-10T15:22:21.320 に答える
1

私はC++の方が好きです。私は Java や C# などを使用してきました。プログラムがこのような OO 環境でより洗練されるにつれて、多重継承が不足していることに気づきました。それは私の主観的な経験です。

驚くべきスパゲッティ コードを作成できます...驚くほどエレガントなコードを作成できます。

于 2008-10-10T23:08:36.123 に答える
1

いいえ、私たちはそれから離れました。あなたは今それを必要としています。

于 2008-10-10T22:35:34.520 に答える
1

十分なROIを提供しないと、物事が複雑になりすぎると思います. あまりにも深い継承ツリーで .NET コードを解体する人々は既に見られます。人々が多重継承を行う力を持っていたら、私は残虐行為を想像することができます.

可能性があることは否定しませんが、十分なメリットが見られません。

于 2008-10-10T14:55:05.930 に答える
1

確かに役立つ場合もありますが、ほとんどの場合、必要だと思っていても実際には必要ないことがわかりました。

于 2008-10-10T14:56:59.110 に答える
1

本当にシンプルだと思います。他の複雑なプログラミング パラダイムと同様に、それを誤用して自分を傷つける可能性があります。オブジェクトを誤用することはできますか (そうです!)。しかし、それは OO 自体が悪いという意味ではありません。

MIも同様。継承されたクラスの大きな「ツリー」がない場合、または同じ名前のメソッドを提供する多くのクラスがない場合は、MI で問題ありません。実際、MI が実装を提供するため、多くの場合、デリゲートされたオブジェクトにメソッドを再コーディングしたり、カット アンド ペーストしたりする必要がある SI 実装よりも優れています。このような場合は、コードが少ない方がよい..インターフェイスの継承を通じてオブジェクトを再利用しようとすることで、コードを共有するという非常に混乱を招く可能性があります。そして、そのような回避策は正しくありません。

.NET の単一継承モデルには欠陥があると思います。インターフェイスのみ、または MI のみを使用するべきでした。「半分ずつ」(つまり、単一の実装の継承と複数のインターフェイスの継承) を持つことは、必要以上に混乱を招き、まったくエレガントではありません。

私は MI のバックグラウンドを持っていますが、それを怖がったり、焦ったりすることはありません。

于 2008-10-10T15:32:02.953 に答える
1

私はこれをここに数回投稿しましたが、本当にクールだと思います。ここでMI を偽造する方法を学ぶことができます。また、この記事は、たとえそれが意図されていなかったとしても、なぜ MI がそれほど苦痛なのかを強調していると思います。

私はそれを見逃すことも必要とすることもありません。目的を達成するためにオブジェクトの構成を使用することを好みます。それがこの記事のポイントでもあります。

于 2008-10-10T15:32:13.297 に答える
1

私自身も C++ で多重継承を使用しましたが、特に祖父母を共有する 2 つの基本クラスがある場合は、トラブルに巻き込まれないように、自分が何をしているのかを本当に知っておく必要があります。次に、仮想継承の問題が発生し、チェーンを呼び出すすべてのコンストラクターを宣言する必要があります (これにより、バイナリの再利用がはるかに難しくなります)...混乱する可能性があります。

さらに重要なことは、現在の CLI の構築方法では、MI を簡単に実装できないことです。彼らが望むならそれができると確信していますが、多重継承よりもCLIで見たいものが他にあります。

Null 非許容参照型など、 Spec#のいくつかの機能が含まれていることを確認したいと思います。また、パラメーターを const として宣言できるようにすることと、関数 const を宣言できるようにすることで、オブジェクトの安全性をさらに高めたいと考えています (つまり、オブジェクトの内部状態がメソッドによって変更されないことを保証していることを意味します)。コンパイラはあなたをダブルチェックします)。

単一の継承、複数のインターフェイスの継承、ジェネリック、および拡張メソッドの間で、必要なことはほとんど何でもできると思います。MIを望んでいる人のために何か改善できることがあるとすれば、集約と構成を容易にするある種の言語構造が必要だと思います。そうすれば、共有インターフェイスを持つことができますが、通常は継承元となるクラスのプライベート インスタンスに実装を委任できます。現在、それを行うには多くの定型コードが必要です。そのためのより自動化された言語機能があれば、非常に役立ちます。

于 2008-10-10T15:48:06.200 に答える
1

C#に暗黙的に指定すると、複数の継承や、その問題に関する継承を見逃すことはありません。

于 2008-12-12T14:07:42.680 に答える
1

いいえ、私はしません。必要なものを開発するために、他のすべての OO 機能を使用します。私はインターフェイスとオブジェクトのカプセル化を使用しており、やりたいことが制限されることはありません。

于 2008-12-12T14:09:44.783 に答える
1

私は継承を使用しないようにしています。毎回できることは少ないです。

于 2008-12-12T14:10:28.463 に答える
0

インターフェイスに実装があれば、物事が簡単になり、コードが少なくなります。しかし、それらは適切に使用されるよりも誤って使用される可能性がありますか? ちゃんとできるまでは見逃さないと思います。多重継承は安全ではないという議論にイライラします。そうではない。しかし、ほとんどすべての言語実装はそうです。しかし、私たちはそれが必要ですか?、わかりません。

私は戻り値の型の共分散を好みます。これは簡単に追加でき (オーバーライドされたメソッドの戻り値の型でルールを緩和するだけです)、常に安全です。

例:

class shape {}

class circle : shape {}

interface part {
    shape Form();
}

interface wheel : part {
    circle Form();
}
于 2011-02-02T17:05:34.243 に答える
0

多重継承を導入すると、C++ の古い Diamond 問題に再び直面することになります...

ただし、それが避けられないと考える人のために、構成によって複数の継承効果を導入することができます(オブジェクト内の複数のオブジェクトを構成し、構成されたオブジェクトに責任を委譲して返すパブリックメソッドを公開します)...

では、わざわざ複数の継承を行い、コードを避けられない例外に対して脆弱にする必要があります...

于 2009-05-29T11:42:28.717 に答える
0

C# の多重継承はほとんど見逃せません。

多重継承を使用して実際のドメイン モデルを定義している場合、私の経験では、単一継承といくつかの優れた設計パターンを使用して、同等に優れた設計を作成できることがよくあります。

多重継承が本当に価値があると私が考える場所のほとんどは、それがドメイン自体ではなく、MI の使用を必要とする何らかのテクノロジー/フレームワークの制約である場所です。ATL を使用した COM オブジェクトの実装は、多重継承を使用して、COM オブジェクトが必要とするすべての必要なインターフェイスを実装する非常に良い例であり、(.NET と比較して) 醜いテクノロジに対する洗練されたソリューションです。C++ フレームワークであるという観点からはエレガントです。

ただし、多重継承を使用できる状況になりました。他のドメイン オブジェクトから機能を派生させる必要があるクラスが 1 つありますが、クロス AppDomain 呼び出しにもアクセスできる必要があります。これは、MarshalByRefObject から継承しなければならないことを意味します。したがって、この特定のケースでは、MarshalByRefObject と特定のドメイン オブジェクトの両方から派生させたいと考えています。ただし、それは不可能なので、クラスはドメイン オブジェクトと同じインターフェイスを実装し、呼び出しを集約されたインスタンスに転送する必要があります。

しかし、私が言ったように、これはテクノロジー/フレームワークが制約を課すケースであり、ドメイン モデル自体ではありません。

于 2009-09-11T08:56:43.343 に答える
-2

インターフェイスは多重継承です。実際のところ、Java/C# タイプのインターフェースは、多重継承の「適切な」実装であると考えています。複数の具象クラスまたは抽象クラスからの継承を許可するのではなく、インターフェイスを使用して多重継承を強制すると、開発者はコードの再利用のために構成/委任を使用する必要があります。継承はコードの再利用に使用すべきではありません。また、C++ 型の多重継承がないため、開発者はより適切に設計されたクラスを考え出す必要があります。

于 2008-10-10T23:16:52.673 に答える