Mit Iterator auf Member-Funktion zugreifen



  • Hey Leute,
    ich habe ein Problem, ich weiß nicht, bzw bekomme es nicht hin, mit einem Iterator auf eine Memberfunktion einer Klasse zuzugreifen.

    als Beispiel:
    [cpp]
    std::vector<myClass*> data;
    std::vector<myClass*>::iterator theIt;

    for( theIt = data.begin(); theIt != data.end(); ++theIt )
    {
    theIt->getName(); // getName ist eine Memberfunktion der Klasse data;
    }
    [cpp]

    Jedoch geht dies leider nicht, ich bekomme immer den Fehler:
    error: request for member 'getName' in '*(((_gnu_cxx::_normal_iterator<Item**......

    Hoffe ihr könnt mir helfen.
    Klar ich könnte das ganze auch mit dem Index operator [] lösen, aber geht das nicht auch mit dem Iterator=

    Danke schonmal=)



  • std::vector<myClass*> data;
    std::vector<myClass*>::iterator theIt;
    
    for( theIt = data.begin(); theIt != data.end(); ++theIt )
    {
      (*theIt)->getName(); // getName ist eine Memberfunktion der Klasse data;
    }
    

    Oder wenn es möglich ist die boost::ptr-container verwenden.



  • Vielen Dank, hat einwandfrei funktioniert;-)

    Habe aber noch eine andere Frage, kann ich auch in einer Schleife wie oben Suchen, ob das Objekt auf das der Iterator zeigt ein bestimmtes ist, und falls ja dieses löschen?

    Funktioniert

    delete (*theIt);
    

    ?

    Danke schonmal



  • Nein, delete darfst du nur auf Zeiger anwenden, deren Speicherbereich du mit new angefordert hast. Was du suchst, ist die Memberfunktion erase() , nachzulesen auf www.cplusplus.com. Vorsicht, die Löschung invalidiert meist darauffolgende Iteratoren, also beim Durchiterieren den Rückgabewert von erase() einsetzen.



  • Ok erst mal Danke=)

    Folgendes Beispiel:

    void Inventar::itemLoeschen()
    {
        for( theIt = items.begin(); theIt != items.end(); ++theIt )
        {
            if( ( *theIt )->getVerfuegbarkeit() == 0 )
            {
                theIt = items.erase( theIt );
                cout << "\n" << ( *theIt )->getName() << " wurde gelöscht\n";
            }
        }
    }
    

    Wenn ich das ganze so löse, dann wird ja theIt nachdem ein Element gelöscht wurde, wird ja theIt auf das Element danach gerichtet.
    So aber dann wird ja nachdem Schleifendurchlauf wieder um eins erhöht, es heißt das eine Element nach dem gelöschen wird übersprungen.

    Soll ich das ganze dann so lösen:

    void Inventar::itemLoeschen()
    {
        for( theIt = items.begin(); theIt != items.end(); ++theIt )
        {
            if( ( *theIt )->getVerfuegbarkeit() == 0 )
            {
                items.erase( theIt );
                cout << "\n" << ( *theIt )->getName() << " wurde gelöscht\n";
            }
        }
    }
    

    Funktioniert das so?
    Danke schonmal



  • Nein. Eher so:

    void Inventar::itemLoeschen()
    {
        for( theIt = items.begin(); theIt != items.end(); )
        {
            if( ( *theIt )->getVerfuegbarkeit() == 0 )
            {
                theIt = items.erase( theIt );
                cout << "\n" << ( *theIt )->getName() << " wurde gelöscht\n";
            }
            else
              ++theIt;
        }
    }
    


  • fr33g schrieb:

    Soll ich das ganze dann so lösen:

    void Inventar::itemLoeschen()
    {
        for( theIt = items.begin(); theIt != items.end(); ++theIt )
        {
            if( ( *theIt )->getVerfuegbarkeit() == 0 )
            {
                items.erase( theIt );
                cout << "\n" << ( *theIt )->getName() << " wurde gelöscht\n";
            }
        }
    }
    

    Nein, das macht deinen Iterator ungültig. Sobald erase() zum Aufrufer zurückkehrt, ist jeder Zugriff auf folgende Iteratoren und insbesondere theIt selbst undefiniert.

    Eine Lösung könnte so aussehen:

    void Inventar::itemLoeschen()
    {
        for( theIt = items.begin(); theIt != items.end(); /* nichts */)
        {
            if( ( *theIt )->getVerfuegbarkeit() == 0 )
                theIt = items.erase( theIt );
            else
                ++theIt;
        }
    }
    

    Für genau diesen Zweck existiert übrigens der STL-Algorithmus std::remove_if() .

    bool itemNichtVerfuegbar(const Item* item)
    {
        return (item->getVerfuegbarkeit() == 0);
    }
    
    void Inventar::itemLoeschen()
    {
        std::remove_if(items.begin(), items.end(), &itemNichtVerfuegbar);
    }
    

    Wichtig: Ich bin hier von nicht-besitzenden Zeigern ausgegangen.

    Falls du die Zeiger mit new anforderst, musst du sie mit delete freigeben. Dadurch wird die Sache unnötigerweise massiv komplizierter und fehleranfälliger, vermeide manuelle Speicherverwaltung wo möglich. Falls der Container die Gegenstände tatsächlich besitzen sollte: Gibt es einen Grund, weshalb du std::vector<Item*> und nicht std::vector<Item> benutzt? Bei der zweiten Variante brauchst du dich nämlich nicht um Speicher zu kümmern.

    Übrigens, aber das ist eher Geschmackssache: Ich persönlich würde mich für eine Sprache entscheiden. Also "RemoveItem" oder "LoescheGegenstand" (wobei ich zu englisch tendiere). Finde ich einfach konsistenter.



  • Erst mal Danke für die Antworten,
    zu der Frage warum ich zeiger auf Items im Vektor speichere.
    Item ist die Basisklasse, diese hat nun wieder 3 Kindklassen, welche wieder eigene Kindklasse haben. Ich dachte wenn ich jetzt im Vektor nur Items speichere, dann kann ich auch nur Items und nich die Spezialisierung, sprich ich müsste für jede Klasse nen eigenen Vektor erstellen.
    Oder bin ich da der Annahme falsche und das funktioniert als wie ich es mit einem Zeiger auf Item löse?



  • Es ist schon richtig das du Zeiger verwendest in diesem Fall, aber eventuell solltest du dabei auf einen Smart Pointer zurückgreifen um memory leaks zu vermeiden. boost::shared_ptr<> z.B.



  • Ok, habe schon öfters was von Smart Pointer gehört, jedoch weiß ich nicht was sie sind und wie man sie benutzt.
    Aber werde mich da jetzt mal im Internet schlau machen und sie gegeben falls nutzen.
    Weiß jetzt immerhin dass mein Ansatz das mit Zeigern zu lösen richtig war=)

    Leute, ich bekomme die Boost Bibliothek irgendwie nicht installiert.
    Hab schon reichlich gegoogelt und auch getStarted bei boost durchgelesen.

    Ich soll doch die build.bat ausführen, und dannach die daraus resultierende exe
    Jedoch komm ich soweit gar nicht, bekomme immer beim ausführen der build.bat fehler:

    http://img7.imageshack.us/img7/8372/boostfehler.png

    Hoffe ihr könnt mir helfen.



  • "Boost installieren" ist in den meisten Fällen überflüssig. Du extrahierst die Header die du brauchst und bindest die in dein Objekt ein. Fertig.

    Sachen wie boost::signals oder boost::filesystem brauchen hingegen eine kompilierte Bibliothek. Aber das interessiert dich im Moment noch nicht.
    shared_ptr kommt ohne lib zurecht.



  • Wie meinst du ich extrahiere die Header-Datei???
    Und wo muss ich sie hinextrahieren, bzw kopieren?
    In das mingw verzeichnis?



  • Wohin spielt keine Rolle, du musst den Pfad nur in deinen Compiler Settings in den Include Suchpfad aufnehmen.



  • Boost kommt doch in einem riesigen fetten .tar.bz2 (oder .zip, weiß grad nicht ob die für Windows was eigenes schnüren...)
    Das entpacken und in das erstellte Verzeichnis reingehen. Dort das Verzeichnis "boost" in deinen Projekttree kopieren. Danach kannst du alles rausschmeißen was du nicht brauchst. Schau in den Header "boost/shared_ptr.hpp", was includiert wird. So kannst du die weiterhangeln. Übrig bleiben wird eine Hand voll Headerdateien. Die brauchen nicht wirklich viel Platz, und wenn du dein Projekt auslieferst muss sich derjenige nicht auch gleich boost zum Kompilieren zulegen...
    Alternativ boost entzippen und das Verzeichnis bei den Include-Pfaden mit angeben.
    Siehe auch hier
    http://www.boost.org/doc/libs/1_42_0/more/getting_started/windows.html#header-only-libraries



  • Wenn man nur besitzende Zeiger in Containern speichern will, sind Boosts Pointer-Container weitaus sinnvoller als Smart-Pointer. Die Benutzung ist intuitiver, weil die Schnittstelle weitgehend zur STL kompatibel ist und man nicht zusätzlich dereferenzieren muss. Nullzeiger sind standardmässig verboten. Ausserdem sind die Pointer-Container exceptionsicher und haben keinen unnötigen Overhead für eine Shared-Semantik.

    Kurz: Sie sind für den Fall hier geradezu ideal.

    boost::ptr_vector<Item> items;
    items.push_back(new SpecialItem); // SpecialItem erbt von Item
    
    items[0].DoSomething(); // operator. und nicht operator->
    
    boost::ptr_vector<Item>::iterator itr = items.begin();
    itr->DoSomething(); // itr->DoSomething() und nicht (*itr)->DoSomething()
    


  • Hey Leute,
    vielen Dank für die vielen Infos und Tipps.
    Werd mich jetzt mal bissel durch die Boost Doku durchkämpfen und sie dann in meinem Projekt einsetzen;-)
    Danke schonmal=)



  • fr33g schrieb:

    Hey Leute,
    vielen Dank für die vielen Infos und Tipps.
    Werd mich jetzt mal bissel durch die Boost Doku durchkämpfen und sie dann in meinem Projekt einsetzen;-)
    Danke schonmal=)

    Ich freu´ mich schon auf die Aha!, Oha! und Nein, sowas! Effekte 😉


Anmelden zum Antworten