Chromanoid hat geschrieben:Was wäre wenn beide IBaseInterface und IOtherBaseInterface Implement überschreiben und dann in einer Klasse zusammenkommen? Also das klassische Diamond-Pattern meine ich. Da würde dann je nach Aufrufer entweder die Methode aus IBaseInterface oder aus IOtherBaseInterface aufgerufen weden, oder? Das hört sich für mich nach einem ziemlichen Alptraum an...
Nun, C++ erlaubt dir, so etwas zu bauen, die Frage ist, wieso du so etwas würdest bauen wollen. Aber wenn du wirklich meinst, dass du sowas unbedingt haben musst, kannst du es bauen und wenn du es baust, dann wird C++ sich auf wohldefinierte und sinnvolle Art und Weise und insbesondere
in sich konsistent verhalten ("sinnvolles" Verhalten derart komplexer Gebilde ist notwendigerweise halt dementsprechend komplex; aber du hast dir dein Gebilde dann halt auch selbst so geschaffen). Generell ist der ganze Vererbungsmechanismus in C++ wesentlich weiter durchdacht als in den meisten anderen Sprachen. Mein Lieblingsbeispiel: Aufruf von virtuellen Methoden aus einem Basisklassenkonstruktor. Wenn ich beispielsweise in Java oder C# in einem Basisklassenkonstruktor eine virtuelle Methode aufrufe, dann führt dieser Aufruf mich immer in den final Overrider des most-derived Type. Vielleicht was man naiv erwarten würde, aber ist das wirklich sinnvolles Verhalten? Man bedenke: Das abgeleitete Objekt wurde zum Zeitpunkt da der Basisklassenkonstruktor läuft noch nicht initialisiert, was bedeutet, dass die Regeln der Sprache selbst verlangen, dass in diesem Fall eine Methode auf einem potentiell ungültigen Objekt aufgerufen wird. Können wir in dieser Situation wirklich nichts sinnvolleres tun? Überlegen wir mal: Das abgeleitete Objekt wurde zum Zeitpunkt da der Basisklassenkonstruktor läuft noch nicht initialisiert, das im Moment in Konstruktion befindliche Objekt kann also noch nicht als ein gültiges Objekt vom abgeleiteten Typ betrachtet werden. Eine virtuelle Methode aus einem Basisklassenkonstruktor in eine abgeleitete Klasse zu dispatchen ist, wenn wir mal ernsthaft drüber nachdenken, eigentlich das so ziemlich am wenigsten sinnvolle Verhalten, das man sich nur vorstellen kann, da es schon rein auf konzeptioneller Ebene unweigerlich die Durchführung einer undefinierten Operation (aufruf einer Methode auf uninitialisiertem Objekt) forciert. Ich würde argumentieren, dass selbst einfach das Verbieten eines solchen Aufrufs per Compile Error wesentlich sinnvoller wäre als das Verhalten von Java und C#. Ginge es noch sinnvoller als das? Nun, was passiert denn während der Konstruktion eines Objektes? Das anfangs komplett uninitialisierte Objekt wird mit jedem Basisklassenkonstruktor zu einem mehr und mehr abgeleiteten Objekt, bis wir am Ende das vollständig abgeleitete Objekt erhalten. Der dynamische Typ des Objektes wird also mit jedem Basisklassenkonstruktor zum mehr und mehr abgeleiteten Typ. Was ist eine virtuelle Methode? Per Definition eine Methode deren Aufruf immer zum final Overrider der Methode im dynamischen Typ des gegebenen Objektes führt. Wie wir gerade festgestellt haben, ändert der dynamische Typ eines Objektes sich logischerweise während der Konstruktion eines Objektes vom am wenigsten abgeleiteten hin zum am meisten abgeleiteten Typ. Der dynamische Typ unseres Objektes in einem Basisklassenkonstruktor ist logischerweise der Typ der Basisklasse in deren Konstruktor wir uns befinden. An dieser Stelle müssen wir uns die Frage stellen, inwiefern das Verhalten von Java und C# nicht eigentlich sogar im Widerspruch zu deren eigenem Typsystem steht, womit es nicht nur wenig sinnvoll sondern vor allem auch noch inkonsistent mit dem Rest der Sprache wäre. Das an diesem Punkt einzige Verhalten, das sowohl konsistent mit dem Konzept von Type an sich als auch der Definition von virtuellen Methoden ist, ist genau das Verhalten das C++ uns gibt, nämlich dass der Aufruf einer virtuellen Methode aus einem Basisklassenkonstruktor die Methode der Basisklasse aufruft, selbst wenn die virtuelle Methode in einer abgeleiteten Klasse überschrieben wurde...
Fazit: Vererbung in C++ hat – imo unverdienterweise – einen schlechten Ruf. Fakt ist, dass Vererbung einfach rein prinzipiell eine extrem komplexe Angelegenheit ist und diese Komplexität sich in einer Sprache, die einen in sich konsistenten Mechanismus dafür anbieten will, notwendigerweise entsprechend widerspiegelt. Komplexität ist, entgegen populärer Erwartungshaltungen, eine einem Problem inhärente Eigenschaft die sich nicht wegzaubern lässt. Der einzige Weg, mit Komplexität umzugehen, ist, sich ihr zu stellen. C++ gibt einem mächtige Werkzeuge in die Hand; es obliegt wie immer der Verantwortung des Programmierers, zu verstehen, was er macht...