prä oder postinkrementieren/dekrementieren in schleifen?



  • Ich hab schleifen bis jetzt immer so implementiert:

    // 500 mal, also von 0 bis 499
    for (int n=0; n<500; n++)
    

    Dies ist schön lesbar und entspricht auch gleich der Zählweise in z.B. Arrays

    auch mit Iteratoren geht das ziemlich gut

    std::vector<int> intv;
    std::vector<int>::iterator i;
    for (i=intv.begin(); i!= intv.end(); i++)
    

    Klärt mich mal bite auf 😕



  • Kommt auf die Situation an:

    for(MapItor itr = c.begin(); itr != c.end(); ){
        if(itr->second == "bla") c.erase(itr++)
        else ++itr;
    }
    

    Is Beispiel aus "Spieleprogrammierung Gems 1", so wird sicherere Iteration gewährleistet.



  • effe_eichelt schrieb:

    Kommt auf die Situation an:

    for(MapItor itr = c.begin(); itr != c.end(); ){
        if(itr->second == "bla") c.erase(itr++)
        else ++itr;
    }
    

    Is Beispiel aus "Spieleprogrammierung Gems 1", so wird sicherere Iteration gewährleistet.

    Aha und was spricht gegen

    for( MapItor itr = c.begin(); itr != c.end(); ++itr )
       if( itr->second == "bla" )
            c.erase( itr );
    


  • Post-Inkrement gibt eine Kopie des alten Wertes zurück die für erase() genutzt wird, während der Iterator schon eins weiter ist, denn wenn du ein Element auf das du gerade zeigst löscht wird der Iterator ungültig.



  • ... schrieb:

    effe_eichelt schrieb:

    Kommt auf die Situation an:

    for(MapItor itr = c.begin(); itr != c.end(); ){
        if(itr->second == "bla") c.erase(itr++)
        else ++itr;
    }
    

    Is Beispiel aus "Spieleprogrammierung Gems 1", so wird sicherere Iteration gewährleistet.

    Aha und was spricht gegen

    for( MapItor itr = c.begin(); itr != c.end(); ++itr )
       if( itr->second == "bla" )
            c.erase( itr );
    

    Alles falsch. Nach dem löschen sollte man den Iterator auf den Rückgabewert von erase setzen.

    Return Value

    A random-access iterator that designates the first element remaining beyond any elements removed, or a pointer to the end of the (container) if no such element exists.

    Bye, TGGC (Demo or Die)



  • @TGGC
    Der Name MapIterator sowie der Zugriff itr->second deuten stark darauf hin, dass es sich hier um einen Iterator in einen assoziativen Container wie z.B. std::map handelt. Schaust du dir nun aber std:🗺:erase an (hier kann google weiterhelfen), wirst du feststellen, dass die passende Methode void, also nichts, zurückliefert. Diese Tatsache macht es extrem schwer "den Iterator auf den Rückgabewert von erase" zu setzen. Die vorgestellte Variante ist also genau richtig, wenn es darum geht in einer Schleife nur bestimmte Elemente aus einer Map zu löschen.



  • HumeSikkins schrieb:

    @TGGC
    Der Name MapIterator sowie der Zugriff itr->second deuten stark darauf hin, dass es sich hier um einen Iterator in einen assoziativen Container wie z.B. std::map handelt. Schaust du dir nun aber std:🗺:erase an (hier kann google weiterhelfen), wirst du feststellen, dass die passende Methode void, also nichts, zurückliefert. Diese Tatsache macht es extrem schwer "den Iterator auf den Rückgabewert von erase" zu setzen. Die vorgestellte Variante ist also genau richtig, wenn es darum geht in einer Schleife nur bestimmte Elemente aus einer Map zu löschen.

    Wenns "einfach" sein soll, was spricht gegen die Verwendung von remove() mit
    anschliessendem erase() ??



  • HumeSikkins schrieb:

    @TGGC
    Der Name MapIterator sowie der Zugriff itr->second deuten stark darauf hin, dass es sich hier um einen Iterator in einen assoziativen Container wie z.B. std::map handelt. Schaust du dir nun aber std:🗺:erase an (hier kann google weiterhelfen), wirst du feststellen, dass die passende Methode void, also nichts, zurückliefert. Diese Tatsache macht es extrem schwer "den Iterator auf den Rückgabewert von erase" zu setzen. Die vorgestellte Variante ist also genau richtig, wenn es darum geht in einer Schleife nur bestimmte Elemente aus einer Map zu löschen.

    @umeSikkins
    Der Name MapIterator sowie der Zugriff itr->second deuten stark darauf hin, dass es sich hier um einen Iterator in einen assoziativen Container wie z.B. std::map handelt. Schaust du dir nun aber std:🗺:erase an (hier kann google weiterhelfen), wirst du feststellen, dass die passende Methode iteraor, also einen iterator, zurückliefert. Diese Tatsache macht es extrem leicht "den Iterator auf den Rückgabewert von erase" zu setzen. Die vorgestellte Variante ist also genau richtig, wenn es darum geht in einer Schleife nur bestimmte Elemente aus einer Map zu löschen.

    Bye, TGGC (Demo or Die)



  • @TGGC|_work
    Im C++ Standard findest du unter 23.3.1 "Template class map" im Abschnitt "modifiers" genau drei erase-Funktionen.

    void erase(iterator pos);
    size_type erase(const key_type& x);
    void  erase(iterator first, iterator last)
    

    Und der Standard ist nun mal das Dokument, dass im *Standard*-C++ Forum die größte Relevanz hat. Weleche Doku hat dir denn was falsches suggeriert?



  • Redhead schrieb:

    Wenns "einfach" sein soll, was spricht gegen die Verwendung von remove() mit anschliessendem erase() ??

    Was meinst du? Meinst du das "erase-remove-idiom"? Falls ja, das funktioniert nicht bei assoziativen Containern.



  • HumeSikkins schrieb:

    Und der Standard ist nun mal das Dokument, dass im *Standard*-C++ Forum die größte Relevanz hat.

    Stimmt, die Realität sollte man ignorieren, es gibt nur eine Wahrheit... 😎

    Bye, TGGC (Demo or Die)



  • TGGC|_work schrieb:

    HumeSikkins schrieb:

    Und der Standard ist nun mal das Dokument, dass im *Standard*-C++ Forum die größte Relevanz hat.

    Stimmt, die Realität sollte man ignorieren, es gibt nur eine Wahrheit... 😎

    Nö. Nur sollte man schon prüfen, was Realität ist.
    Schauen wir mal:
    Gnu-STL, STL-Port, SGI-STL, comolib: void erase
    Dinkumware-STL: iterator erase.

    Fazit: Wer ausschließlich mit dem Visual Studio arbeitet oder aber überall eine Dinkumware-STL installiert (erst kaufen), der kann auf einen Rückgabwert vom Typ iterator bauen.
    Wer allerdings nebenbei mit einem gcc, einem sun-cc oder irgendeinem anderen Compiler arbeitet, der sollte in diesem Punkt wohl besser beim Standard bleiben.



  • HumeSikkins ist das hier ok?

    for(MapIterator itr = c.begin(); itr != c.end(); ++itr)
    {
       if(itr->second == "bla")
       {
           c.erase(itr);
       }
    }
    


  • ... schrieb:

    HumeSikkins ist das hier ok?

    for(MapIterator itr = c.begin(); itr != c.end(); ++itr)
    {
       if(itr->second == "bla")
       {
           c.erase(itr);
       }
    }
    

    Nein. Durch die erase-Operation wird der Iterator ungültig. Der nachfolgende Aufruf des Inkrement-Operators führt dann zu undefiniertem Verhalten.



  • Ich bin zwar nicht HumeSikkins, aber trotzdem: nein. c.erase(it) zerlegt den übergebenen Iterator, danach dürfte er nicht mehr auf ein gültiges Element des Containers zeigen - und danach brauchst du viel Glück (und eine stabil implementierte Bibliothek), um noch sinnvoll mit it weiterarbeiten zu können.

    Du mußt deinen Zähl-Iterator also schon weitergerechnet haben, bevor du eine Kopie davon an c.erase() übergeben kannst - und das schaffst du z.B. mit effe_eichelt's Code.



  • Ah 💡 Endlich hab ichs gerafft. Danke. 🙂



  • TGGC schrieb:

    Bitte.

    Bye, TGGC (Demo or Die)



  • TGGC|_work schrieb:

    TGGC schrieb:

    Bitte.

    Bye, TGGC (Demo or Die)

    😃 😕 👎


Anmelden zum Antworten