Poloymorphismus + zugriff auf abgeleitete "Klassenvariablen"



  • Hallo,

    ich habe folgendes Problem:
    Diese zwei Klasses habe ich (unten) Es sind übrigens noch mehr abgeleitet Klassen, aber die sind jetzt erstmal egal.

    In einem Array speicher ich Pointer auf Feld, also: CFeld temp;*
    Danach gehe ich durch das Array und weise je nachdem welchen Typ ich haben will, die entsprechende abgeleitet Klasse zu, also in dem Fall
    CStartFeld: temp = new CStartfeld();
    Wie komme ich jetzt über temp-> oder so ähnlich an die Variablen der abgeleiteten Klasse ran (iAnzahl, sSpielStein[])?

    😕 😕 😕

    Vielen Dank!!!

    class CFeld {    //Superklasse
    public:    
        CFeld(int, int, int);
    
        void setNord(CFeld* Nord);
        void setOst(CFeld* Ost);
        void setSued(CFeld* Sued);
        void setWest(CFeld* West);
    
       int getSpalte();
       int getZeile();
       int getType();
    
       CFeld* getNord();
       CFeld* getOst();
       CFeld* getSued();
       CFeld* getWest();
    
       CStein* getStein();
    
       void setStein(CStein*);
    
    private:
        CFeld* Nord;   //Zeiger nach Norden
        CFeld* Ost;      //Zeiger nach Osten
        CFeld* Sued;   //Zeiger nach Sueden
        CFeld* West;   //Zeiger nach Westen
    
        bool blockierbar;
        int iSpalte;
        int iZeile;
        int iType;
        CStein *sStein;
    };
    
    class CStartFeld:public CFeld        //abgeleitete Klasse
    {
    public:
       CStartFeld(int, int, int);
    private:
       CStein* sSpielStein[5];
       static int iAnzahl;
    };
    

    [ Dieser Beitrag wurde am 09.05.2003 um 12:04 Uhr von Dimah editiert. ]



  • Lass das C vor dem Klassennamen weg. Wieso? -> Suchfunktion

    Du kannst dynamic_cast benutzen.

    Feld* temp;
    /* ... */
    try
    {
      (dynamic_cast<StartFeld&>(*temp)).StartFeldFunktion();
    }
    catch(...) // was kommt hier nochmal rein? bad_cast?
    {
      // Fehler beim casten, temp ist kein StartFeld
    }
    


  • Also ganz versteh ich das Problem nicht.

    Aber wenn du einen Pointer auf eine Klasse hast, greifst du ganz normal mit Klasse->attribut/Methode() zu. Dabei ist es egal, ob diesttribute von der Mutterklasse ist oder von der abgeleiteten, solange sie als public deklariert sind.

    Methoden die abgeleitet werden, empfehlen sich meist als virtual zu deklarieren, damit sie bei Bedarf, überschrieben werden können.

    Aber wie gesagt deine konkrete Frage wie du an iAnzahl rankommst, einfach so temp::iAnzahl (public Memberattribute der Klasse)

    Mir ist auch nicht ganz klar ist, was temp ist? Der Sinn kommt bei mir nicht so ganz klar rüber. Ist das ein pointer auf ein Objekt der Klasse, oder ein pointer auf ein Array von Pointern.(im einen Fall temp->.. in anderen *(temp+x)-> )
    Grüße Flow



  • Hallo,

    @Flow_cplus
    ich muss in einem CTypedPtrArray<..., *CFeld> verschiedene Felder speichern.
    Also Startfelder, Zielfelder, usw.. Das Problem ist aber, das ich beim anlegen, den Typen, den ich speichern will angeben muss. In diesem Fall ein Pointer auf die Superklasse CFeld. Da ich aber verschiederne FeldTypen speichern will, weise ich diesem Pointer dann Speicher von CStartFeld(), CZielFeld, ... zu.
    Also so. Sorry, kann das ein bisschen schlecht erklären.
    *CFeld temp = new CStartFeld();
    Wenn ich jetzt mit temp->attribut/Methode() auf eine Methode bzw. Attribut zugreifen will, kann ich nur auf die Methoden der Superklasse zugreifen.

    @cd9000
    wenn ich das mit dynamic_cast mache kommt folgende Fehlermeldung:
    error C2683: dynamic_cast : 'CFeld' ist kein polymorpher Typ
    feld.h(9) : Siehe Deklaration von 'CFeld'
    Was muss ich machen, damit das funktioniert.

    Danke, Stefan



  • Original erstellt von Flow_cplus:
    Aber wenn du einen Pointer auf eine Klasse hast, greifst du ganz normal mit Klasse->attribut/Methode() zu. Dabei ist es egal, ob diesttribute von der Mutterklasse ist oder von der abgeleiteten, solange sie als public deklariert sind.

    nicht ganz:

    struct foo {
       virtual void bar () {}
    };
    
    struct faz : foo {
        virtual void baz () {}
    };
    
    int main () {
    
       foo *foo_zeiger = new faz;
       foo_zeiger->bar (); 
    
       //aber wie baz aufrufen? foo_zeiger zeigt auf ein foo, und foo hat keine
       //methode namens baz. also nach faz downcasten:
    
       dynamic_cast<faz&>(*foo).baz();
    }
    

    dynamic cast wirft std::bad_cast wenn sein template argument eine referenz ist, und gibt 0 zurück, wenn in einen zeiger gecastet werden soll.

    zum problem:
    1)
    du kannst wie schon gesagt downcasten, dann caste aber als zeiger, da eine referenz eher eine zusicherung ausdrückt:

    //Beispiele:
    vector <CFeld*> vec;
    vec.push_back(new CStartFeld(10,20)); //whatever
    vec.push_back(new CSpielFeld); //etc.
    
    for (int i = 0; i < vec.size(); ++i) {
       if (CStartFeld *feld = dynamic_cast<CStartFeld*>(vec[i])) {
          //mach was mit einem CStartFeld
       } else if (CSpielFeld *feld = dynamic_cast<CSpielFeld*>(vec[i])) {
          //mach was mit einem CSpielFeld
       }
    }
    
    //Speicher freigeben...
    

    du weißt, dass du downcasten kannst, aber nicht, wann wohin -> als zeiger
    du weißt genau, dass du jetzt downcasten kannst -> referenz (wenn du dich irrst, wird eine ausnahme geworfen)
    zumindest imho ist das gut!

    obiges sieht nach designfehler aus, du solltest dynamic_cast nur in speziellen Fällen verwenden. [Darf jemand anders etwas ausführlicher beschreiben]
    Außerdem willst du downcasten und dann öffentliche Variablen verwenden? Grober Designfehler!
    Also:

    In der Elternklasse getter/setter Methoden (virtuelle natürlich) dazutun.
    Überhaupt: Wenn du auf einen Zeiger auf eine Basisklasse eine Funktion aufrufst, der auf eine abgeleitete Klasse zeigt, die diese Funktion überschreibt, dann musst du die Funktion in der Basisklasse als virtual deklarieren. Ansonsten wird immer die Funktion der Basisklasse aufgerufen.

    Ich hoffe, das war verständlich genug 🙂



  • Original erstellt von Quatschkopp:
    **
    Wenn ich jetzt mit temp->attribut/Methode() auf eine Methode bzw. Attribut zugreifen will, kann ich nur auf die Methoden der Superklasse zugreifen.
    **

    Polymorphismus ist in C++ mit dem Schlüsselwort 'virtual' implementiert.

    struct base {
       virtual void foo () { /*base::foo*/ }
       void bar () { /*base::bar*/ }
       virtual ~base () {} //Destruktoren virtual machen, da sie immer aufgerufen werden (sollten)
    };
    
    struct derived : base {
       void foo /*virtual ist überflüssig*/ () { /*derived::foo*/ }
       void bar () { /*derived::bar*/ } //ist nur eine überdeckung!
    };
    
    int main () {
       base *b = new derived;
       b->foo (); //ruft derived::foo auf! (wegen virtual)
       b->bar (); //ruft base::bar auf (ist ja nicht virtual!)
       delete *b; //wenn der dtor nicht virtual wäre, würde nur base::~base und niemals derived::~derived aufgerufen werden!
    }
    

    **
    @cd9000
    wenn ich das mit dynamic_cast mache kommt folgende Fehlermeldung:
    error C2683: dynamic_cast : 'CFeld' ist kein polymorpher Typ
    feld.h(9) : Siehe Deklaration von 'CFeld'
    Was muss ich machen, damit das funktioniert.

    Danke, Stefan**

    Um dynamic cast zu nutzen, musst du mindestens eine virtuelle Methode deklarieren(auch definieren?) (ein vtable muss angelegt sein, sonst ermittelt dynamic_cast den falschen Typ)

    und nicht vergessen: destruktor der basisklasse virtuell machen!

    [ Dieser Beitrag wurde am 08.05.2003 um 20:31 Uhr von davie editiert. ]

    [ Dieser Beitrag wurde am 08.05.2003 um 20:32 Uhr von davie editiert. ]



  • Ok danke,

    ich hoffe, dass ich es damit hinbekomme. Ansonsten melde ich mich nochmal.

    Viele Grüße

    Stefan



  • Ok, klappt jetzt alles!

    Nur nochmal eine Frage. Ist es nicht doof, wenn ich eine Superklasse habe,mit den virtuellen abstrakten Funktionen. Von der leite ich ca. 5 Klassen ab. Dann muss ich ja, wenn ich die Funktionen nur in einer Klasse brauche, die Fkt. in den anderen Klassen leer implementieren! Oder?

    MFG Stefan



  • virtuell != virtuell abstrakt

    Wenn eine Memberfunktion nur virtuell ist, aber nicht abstrakt, benutzen die abgeleiteten Klassen die Funktion der Basisklasse, solange diese nicht in der abgleiteten Klasse überschrieben wird.



  • Acho,

    ich dachte auch virtuelle Methoden müssen immmer überschrieben werden!
    Ist OK!

    MFG Stefan



  • CFeld braucht auf jeden fall ein virtulen destuktor
    wenn du nämlich so was machst

    CFeld * p = new CStartfeld();
    delete p;
    

    wird nur der dtor von CFeld aufgerufen aber wir haben hier ein CStartfeld objekt



  • Jo, ich habe alle Detruktoren von Klassen, wo ich ableite, virtuell deklariert. 🙂



  • Original erstellt von cd9000:
    Lass das C vor dem Klassennamen weg. Wieso? -> Suchfunktion

    Warum hört nur keiner auf mich? 😞



  • @ cd9000

    sorry, dann muss ich meine ganzen Klassen ändern.
    Habe bei der Suchfunktion nichts gefunden. Sag mir doch mal warum!

    MFG



  • Der Thread liegt im "Rund um..." Forum und nicht hier:
    7 Seiten Diskussion über Klassenprefix


Anmelden zum Antworten