133 Stimmen

Was genau ist das Problem mit der Mehrfachvererbung?

Ich sehe immer wieder, wie Leute fragen, ob Mehrfachvererbung in die nächste Version von C# oder Java aufgenommen werden soll. C++-Leute, die das Glück haben, diese Fähigkeit zu besitzen, sagen, dass dies so ist, als würde man jemandem einen Strick geben, an dem er sich aufhängen kann.

Was hat es mit der Mehrfachvererbung auf sich? Gibt es konkrete Beispiele?

60 Stimmen

Ich möchte nur erwähnen, dass C++ großartig ist, um Ihnen genug Seil zu geben, um sich selbst aufzuhängen.

1 Stimmen

Eine Alternative zur Mehrfachvererbung, die viele der gleichen Probleme angeht (und, IMHO, auch löst), sind Traits ( iam.unibe.ch/~scg/Forschung/Traits )

54 Stimmen

Ich dachte, C++ gibt Ihnen genug Spielraum, um sich selbst in den Fuß zu schießen.

2voto

supercat Punkte 72939

Eines der Entwurfsziele von Frameworks wie Java und .NET besteht darin, dass Code, der für eine Version einer vorkompilierten Bibliothek kompiliert wurde, ebenso gut mit nachfolgenden Versionen dieser Bibliothek funktioniert, selbst wenn diese nachfolgenden Versionen neue Funktionen hinzufügen. Während das normale Paradigma in Sprachen wie C oder C++ darin besteht, statisch gelinkte ausführbare Dateien zu verteilen, die alle benötigten Bibliotheken enthalten, besteht das Paradigma in .NET und Java darin, Anwendungen als Sammlungen von Komponenten zu verteilen, die zur Laufzeit "gelinkt" werden.

Das COM-Modell, das .NET vorausging, versuchte, diesen allgemeinen Ansatz zu verwenden, aber es gab keine wirkliche Vererbung - stattdessen definierte jede Klassendefinition effektiv sowohl eine Klasse als auch eine gleichnamige Schnittstelle, die alle ihre öffentlichen Mitglieder enthielt. Instanzen waren vom Typ der Klasse, während Referenzen vom Typ der Schnittstelle waren. Die Deklaration einer Klasse als Ableitung von einer anderen war gleichbedeutend mit der Deklaration einer Klasse als Implementierung der Schnittstelle der anderen und erforderte, dass die neue Klasse alle öffentlichen Mitglieder der Klassen, von denen sie abgeleitet war, neu implementierte. Wenn Y und Z von X abgeleitet sind und W von Y und Z abgeleitet ist, spielt es keine Rolle, ob Y und Z die Mitglieder von X unterschiedlich implementieren, da Z nicht in der Lage ist, deren Implementierungen zu verwenden - es muss seine eigenen definieren. W könnte Instanzen von Y und/oder Z kapseln und seine Implementierungen von X's Methoden durch ihre verketten, aber es gäbe keine Unklarheit darüber, was X's Methoden tun sollten - sie würden tun, was auch immer der Code von Z ihnen explizit vorgibt.

Die Schwierigkeit in Java und .NET besteht darin, dass es dem Code erlaubt ist, Mitglieder zu erben und auf sie zuzugreifen implizit beziehen sich auf die übergeordneten Mitglieder. Angenommen, die Klassen W-Z sind wie oben beschrieben miteinander verbunden:

class X { public virtual void Foo() { Console.WriteLine("XFoo"); }
class Y : X {};
class Z : X {};
class W : Y, Z  // Not actually permitted in C#
{
  public static void Test()
  {
    var it = new W();
    it.Foo();
  }
}

Es scheint, als ob W.Test() sollte bei der Erzeugung einer Instanz von W die Implementierung der virtuellen Methode Foo definiert in X . Nehmen wir jedoch an, Y und Z befänden sich in einem separat kompilierten Modul, und obwohl sie wie oben definiert wurden, als X und W kompiliert wurden, wurden sie später geändert und neu kompiliert:

class Y : X { public override void Foo() { Console.WriteLine("YFoo"); }
class Z : X { public override void Foo() { Console.WriteLine("ZFoo"); }

Was soll nun der Aufruf von W.Test() ? Wenn das Programm vor der Verteilung statisch gelinkt werden muss, könnte die statische Linkstufe erkennen, dass das Programm vor der Änderung von Y und Z keine Mehrdeutigkeit aufwies, dass aber die Änderungen an Y und Z die Dinge mehrdeutig gemacht haben, und der Linker könnte sich weigern, das Programm zu erstellen, bis diese Mehrdeutigkeit behoben ist. Andererseits ist es möglich, dass die Person, die sowohl W als auch die neuen Versionen von Y und Z hat, jemand ist, der das Programm einfach nur ausführen will und keinen Quellcode dafür hat. Wenn W.Test() läuft, wäre es nicht mehr klar, was W.Test() tun sollte, aber bis der Benutzer versucht, W mit der neuen Version von Y und Z auszuführen, gäbe es keine Möglichkeit für irgendeinen Teil des Systems, zu erkennen, dass es ein Problem gibt (es sei denn, W wurde schon vor den Änderungen an Y und Z als unzulässig angesehen).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X