Initialisierungen bei Vererbung


  • Mod

    ceplusplus@loggedoff schrieb:

    Also muss ich nun jeweils einen zweiten geeigneten Konstruktor schreiben? Oder wie macht man das normalerweise/am besten?

    Ein zweiter Konstruktor ist sicherlich eine Möglichkeit. Bei komplexeren Argumenten kämen Referenzen in Betracht, generell ist das sowieso nur eine Variante des Forwardingproblems. In der Praxis dürfte das aber selten ein echtes Problem sein - den Konstruktoren von B und C Argumente für die Initialisierung von A mitzugeben impliziert ja, dass B und C nicht abstrakt sind, und das dürfte selten eine korrekte Designentscheidung sein.



  • @(D)Evil

    Das geht nicht:
    "Der Konstruktor einer virtuellen Basisklasse wird mit den Argumenten aufgerufen, die im Basisinitialisierer der zuletzt abgeleiteten Klasse angegeben sind"

    Bei deinem Beispiel müsste Klasse A einen Default-Konstruktor besitzen und würde nicht sofort geeignet initialisiert werden.

    camper schrieb:

    den Konstruktoren von B und C Argumente für die Initialisierung von A mitzugeben impliziert ja, dass B und C nicht abstrakt sind, und das dürfte selten eine korrekte Designentscheidung sein.

    Hm? Von B und C sollen doch genauso Objekte angelegt und verwendet werden können, es sind immerhin schon spezielle Objekte von A.

    Beispiel:
    Klasse A = Roboter
    Klasse B = FahrenderRoboter
    Klasse C = LaufenderRoboter
    Klasse C = MultifunktionalRoboter

    Wenn ich nun einen fahrenden Roboter erstellen möchte, muss auch ein geeigneter Konstruktor für Roboter vorhanden sein...

    MfG



  • ceplusplus@loggedoff schrieb:

    @(D)Evil

    Das geht nicht:
    "Der Konstruktor einer virtuellen Basisklasse wird mit den Argumenten aufgerufen, die im Basisinitialisierer der zuletzt abgeleiteten Klasse angegeben sind"

    Bei deinem Beispiel müsste Klasse A einen Default-Konstruktor besitzen und würde nicht sofort geeignet initialisiert werden.

    Afaik braucht er das nicht - jedes (Teil)Objekt wird genau einmal konstruiert - und der A-Anteil von D direkt aus dem D-Ctor heraus. D.h., B und C können in jedem Fall davon ausgehen, mit einem funktionstüchtigen A arbeiten zu können (selbst wenn dessen Inhalt nicht zu den eigenen Vorstellungen passen sollte).



  • ceplusplus@loggedoff schrieb:

    Von B und C sollen doch genauso Objekte angelegt und verwendet werden können, es sind immerhin schon spezielle Objekte von A.

    Beispiel:
    Klasse A = Roboter
    Klasse B = FahrenderRoboter
    Klasse C = LaufenderRoboter
    Klasse C = MultifunktionalRoboter

    Wenn ich nun einen fahrenden Roboter erstellen möchte, muss auch ein geeigneter Konstruktor für Roboter vorhanden sein...

    MfG


  • Mod

    ceplusplus@loggedoff schrieb:

    ceplusplus@loggedoff schrieb:

    Von B und C sollen doch genauso Objekte angelegt und verwendet werden können, es sind immerhin schon spezielle Objekte von A.

    Beispiel:
    Klasse A = Roboter
    Klasse B = FahrenderRoboter
    Klasse C = LaufenderRoboter
    Klasse C = MultifunktionalRoboter

    Wenn ich nun einen fahrenden Roboter erstellen möchte, muss auch ein geeigneter Konstruktor für Roboter vorhanden sein...

    MfG

    Damit wird das LSP verletzt. Ein FahrenderRoboter läuft niemals (nehm ich jetzt mal an), ein LaufenderRoboter fährt niemals, ein MultifunktionalRoboter, der laufen und fahren kann ist demzufolge weder ein FahrenderRoboter noch ein LaufenderRoboter.



  • Dann muss man die Klassen halt umnennen:

    Roboter
    RoboterWoUnterAnderemFahrenKann : Roboter
    RoboterWoUnterAnderemLaufenKann : Roboter
    RoboterWoLaufenUndFahrenUndVielleichtNochwasAnderesKann : RoboterWoUnterAnderemFahrenKann, RoboterWoUnterAnderemLaufenKann
    

    😃



  • Mhm achso verstehe, der MultifunktionalRoboter IST KEIN LaufenderRoboter ODER FahrenderRoboter.

    Hmm, kennt jemand ein anderes Beispiel?

    Wie wärs mit...

    KFZ
        /  \
    PKW     TRANSPORTER
       \    /
        KOMBI
    

    Kombi IST ein PWK und Kombi IST ein Transporter? Oder nicht? 😕

    MfG



  • Ne ich verstehs doch ned...

    Das Liskov-Prinzip besagt doch, dass das Verhalten der abgeleiteten Klasse identisch mit dem der Basisklasse sein muss.

    Das ist es in meinem obigen Beispiel doch auch. FahrenderRoboter fährt. MultifunktionalRoboter fährt genauso. LaufenderRoboter läuft. MultifunktionalRoboter läuft genauso.

    Habe auch gelesen, dass Mehrfachvererbung selten sinnvoll ist :-| Stimmt das?

    😕

    MfG


  • Mod

    ceplusplus@loggedoff schrieb:

    Mhm achso verstehe, der MultifunktionalRoboter IST KEIN LaufenderRoboter ODER FahrenderRoboter.

    Hmm, kennt jemand ein anderes Beispiel?

    Wie wärs mit...

    KFZ
        /  \
    PKW     TRANSPORTER
       \    /
        KOMBI
    

    Kombi IST ein PWK und Kombi IST ein Transporter? Oder nicht? 😕

    MfG

    Ja und nein. Das Problem ist immer eines der Konkretisierung - in der natürlichen Sprache verwenden wir oft das gleiche Wort für eine abstrakte Kategorie oder etwas Konkreteres (und den Rest schließen wir aus dem Kontext). An deinem Beispiel ist zunächst nichts falsch - das kann erst passieren, wenn du Fehler beim Erstellen von Objekten machst (nur Objekte können sich polymorph verhalten). Ein PKW (das heißt, ein Objekt, dass direkt als PKW instantiiert wird) kann nur Personen transportieren, ein Transporter (...) nur irgendwelche Sachen (in unserem Programm - wenn beides könnten, selbst nur in unterschiedlichem Grade, gäbe es ja keinen Grund, den Unterschied in verschiedene Klassen zu gießen). Ein PKW ist demzufolge leer (und kann Personen aufnehmen), wenn keine Personen drin sind. Ein Transporter ist leer, wenn keine Sachen drin sind (und kann dann beladen werden). Ein Kombi ist nicht unbedingt leer nur weil er keine Personen oder keine Sachen enthält - und ist folglich weder ein PKW noch ein Transporter. Diese Argumentation ist zutreffend, wenn PKW,Transporter und Kombi gleichermaßen konkrete Klassen darstellen von denen vollständige Objekte erstellt werden können. Etwas anderes wäre es, wenn wir hier in diesem Beispiel PKW und Transporter sowohl als Konzept als auch als konkrete Beschreibung eines Transportmittels ansehen:

    KFZ
            /   \
          PKW    TRANSPORTER
         /  \    /     \
    nurPKW   KOMBI     nurTRANSPORTER
    

    In diesem Falle beschreibt PKW nur die Fähigkeit, Personen transportieren zu können - und KOMBI hat diese Fähigkeit auch - aber die Eigenschaft, dass das Fahrzeug leer ist, wenn keine Personen mehr drin sind, kann nicht mehr daraus abgeleitet werden, dass es sich um einen PKW handelt. Nur für nurPKW trifft dies zu. Generell muss eine LSP-Vererbungshierarchie immer vom Abstrakteren zum Konkreteren hin ableiten. Eine Kindklasse, die nicht konkreter als ihre Elternklasse ist, ist entweder mit dieser identisch (dann gibt es keinen Grund, überhaupt erst abzuleiten), oder sie verletzt das LSP. Die gleiche Argumentation kannst du prinzipiell auf jedes andere Beispiel anwenden. Als Grundregel, die wirklich so gut wie keine Ausnahmen hat: Leite niemals von einer Klasse ab, die auch vollständige Objekte erzeugen soll. Verletzungen dieser Regel vermischen Abstraktionsebenen und das ist ein ganz schneller Weg ins Verderben.
    Um zum Ausgangspunkt dieses Threads zurückzukommen: die Klassen B,C sind in diesem Schema keine Blätter des Vererbungsbaumes mehr: da sie nie konkrete Objekte erzeugen (sollen - ob sie abstrakt im Sinne der Sprache sind, ist unerheblich) müssen ihre Konstruktoren evtl. vorhandene Basisklassen nie selbst initialisieren und es ist folglich nicht notwendig, sie mit entsprechenden Argumenten zu füttern.

    P.S. Meine Vererbunghierarchie ist als solche nicht wirklich brauchbar - sie dient nur zur Illustration der Argumentation.



  • Danke für die Erklärung. Ich glaub jetzt hab ichs.
    Man sollte sich also immer zuerst Gedanken über ALLE Eigenschaften jedes Hierarchieknotens machen...

    MfG


Anmelden zum Antworten