Vererbung und Neuerstellung von Klassen



  • hallo,

    folgendes Problem:

    class Mom
    {...};
    
    class KidA
    {...};
    
    class KidB
    {...};
    
    void replace_nth_kid(std::list<Mom*> kids, n)
    {
       Mom * nth_kid = get_nth_kid(kids, n);
       delete nth_kid;
       nth_kid = new <PROBLEM>
       set_nth_kid(nth_kid, n);
    }
    

    an der mit <PROBLEM> gekennzeichneten Stelle möchte ich eine Instanz der Klasse KidA oder KidB erstellen, je nachdem ob der alte Eintrag ein KidA oder KidB war. Wie geht das?



  • Korrigierter Code:

    class Mom
    {...};
    
    class KidA : public Mom
    {...};
    
    class KidB : public Mom
    {...};
    
    void replace_nth_kid(std::list<Mom*> kids, n)
    {
       Mom * nth_kid = get_nth_kid(kids, n);
       delete nth_kid;
       nth_kid = new <PROBLEM>
       set_nth_kid(nth_kid, n);
    }
    


  • Du könntest in KidA und KidB jeweils eine Funktion definieren, die den gewünschten Typ erzeugt.

    class Mom
    {
        virtual Mom* createNew() const = 0;
    };
    
    class KidA : public Mom
    {
       Mom* createNew() const { return new KidA(); }
    };
    
    class KidB : public Mom
    {
       Mom* createNew() const { return new KidB(); }
    };
    
    void replace_nth_kid(std::list<Mom*> kids, n)
    {
       Mom * nth_kid = get_nth_kid(kids, n);
       Mom * new_kid = nth_kid->createNew();
       delete nth_kid;
       nth_kid = new_kid;
       set_nth_kid(nth_kid, n);
    }
    


  • Das ist ja Menschenverachtend, dein Design!!!
    Trotzdem: Eine Lösung geht wohl über eine pure-virtual-Funktion in Mom, die KidA und KidB implementieren müssen.
    Zurückgegeben wird ein identifier (enum, int, oder auch string), anhand dessen ein KidA oder KidB identifiziert werden kann. Diesen mit if()s identifizieren und entsprechend neu erstellen.
    Alternativ prüfen ob ein dynamic_cast<>() nicht NULL zurückgibt.

    Aber Kinder ersetzen... ich bin immer noch ganz entsetzt...



  • Füge in Mom eine virtuelle Funktion ein, die auf dem "alten" Objekt aufrufst und ein neues Objekt erzeugt -- statt das alte Objekt sofort zu löschen.

    void replace_nth_kid(std::list<Mom*> kids, n)
    {
       Mom * nth_kid  = get_nth_kid(kids, n);
       Mom * nth_kid2 = nth_kid->neu(....);
       delete nth_kid;
       set_nth_kid(nth_kid2, n);
    }
    

    dynamic_cast gibt's auch noch. Solltest Du aber vermeiden, wenn's geht.

    Achte auf Ausnahme-Sicherheit (Kann set_nth_kid eine Ausnahme werfen?)

    Mit etwas mehr Kontext drumherum hätte man Dir eventuell eine elegantere Lösung anbieten können.



  • Mit dem virtual Constructor Idiom.

    Edit: zu spät ...



  • uneSCO schrieb:

    Eine Lösung geht wohl über eine pure-virtual-Funktion in Mom, die KidA und KidB implementieren müssen.
    Zurückgegeben wird ein identifier (enum, int, oder auch string), anhand dessen ein KidA oder KidB identifiziert werden kann. Diesen mit if()s identifizieren und entsprechend neu erstellen.

    Eine suboptimale Lösung. Du nutzt Polymorphie, um den Typen zu identifizieren und machst dann ein ggf. großes switch/case-statement um anhand der Identifizierung ein neues Objekt vom richtigen Typ zu erzeugen - warum nicht gleich Polymorphie nutzen um gleich das richtige Objekt zu erzeugen, wie es andere schon gezeigt haben? So gehst du nur den halben Weg.

    Alternativ prüfen ob ein dynamic_cast<>() nicht NULL zurückgibt.

    Was dann die noch schlechtere Variante ist, wo du überhaupt nicht mehr die Hilfsmittel nutzt, die dir die Sprache zur Verfügung stellt, sondern alles gleich mühsam "zu Fuß" machst.



  • pumuckl schrieb:

    Eine suboptimale Lösung. Du nutzt Polymorphie, um den Typen zu identifizieren und machst dann ein ggf. großes switch/case-statement um anhand der Identifizierung ein neues Objekt vom richtigen Typ zu erzeugen - warum nicht gleich Polymorphie nutzen um gleich das richtige Objekt zu erzeugen, wie es andere schon gezeigt haben? So gehst du nur den halben Weg.

    na super, wenn ich das virtual constructor idiom verwende, muss ich genau so viel machen (in jedem meiner N Kinder die copy-funktion implementieren...)



  • Karlheinz Stockhausen schrieb:

    pumuckl schrieb:

    ...

    na super, wenn ich das virtual constructor idiom verwende, muss ich genau so viel machen (in jedem meiner N Kinder die copy-funktion implementieren...)

    Stimmt, aber du musst bei einer zusätzlichen Kindklasse auch nur dort die copy-Funktion implementieren und fertig. Andernfalls müsstest du dort die Klassenid-Funktion implementieren und an jeder Stelle, wo du sie verwendest (das Neuerzeugen wird über kurz oder lang nicht der einzige Fall sein) das switch-staement erweitern. Du müsstest also um eine neue Klasse anzulegen, die anderen Klassen jedesmal wieder anpacken und neu compilieren.



  • Karlheinz Stockhausen schrieb:

    ...na super, wenn ich das virtual constructor idiom verwende, muss ich genau so viel machen ...

    Stimmt ja gar nicht. Wenn Du über "ID" gehst, muss
    - jedes Kind eine "ID"-Funktion bereitstellen (sonst eben eine "new"-Funktion) UND
    - irgendwo eine "Erzeugerfunktion" bereitstellen (und für jedes neue Kind mitpflegen).
    Du hast also eine Funktion(alität) und eine Abhängigkeit mehr.

    Außerdem solltest Du nicht vergessen, dass Du garantiert irgendwann zu dem Problem kommst, dass die "Kinder" über unterschiedliche Ctor-Argumente konstruiert werden ... und dann wird's eng mit dem jetzigen Design (und mit "IDs" erst recht).

    Gruß,

    Simon2.



  • Ich frage besser garnicht wieso ein Kind ein spezialisierung einer Mom ist oder?


  • Administrator

    Im übrigen kann sich beim virtual constructor idom der Rückgabewert der virtuellen Funktion verändern. Kann auch sehr praktisch sein.
    http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8

    Oder schnell auf deinen Code angewendet:

    class Mom
    {
        virtual Mom* clone() const = 0 { /* default implementation? */ };
    };
    
    class KidA : public Mom
    {
       KidA* clone() const { return new KidA(*this); }
    };
    
    class KidB : public Mom
    {
       KidB* clone() const { return new KidB(*this); }
    };
    

    fggdhjfgjhfghf schrieb:

    Ich frage besser garnicht wieso ein Kind ein spezialisierung einer Mom ist oder?

    Naja, wohl aus dem einfachen Gedanken heraus, dass ein Kind womöglich unter Umständen auch eine Mutter sein kann. Allerdings finde ich die Frage nicht unberechtigt. Denn die is-a ist nicht eine womöglich Beziehung 😃
    Auch dass es ein KidA und KidB geben soll, halte ich für ein sehr seltsames Design.
    Aber womöglich waren es auch einfach nur ein wenig schlecht gewählte Platzhalter.

    Grüssli


Anmelden zum Antworten