Iterieren durch STL-List und dabei löschen



  • Hallo,

    folgendes Programm, welches eine STL-List befuellt, ausgibt, einige Elemente löscht und wieder ausgibt.

    #include <list>
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
    list<int> liste;
    liste.push_back(1);
    liste.push_back(2);
    liste.push_back(0);
    liste.push_back(3);
    liste.push_back(4);
    liste.push_back(5);
    liste.push_back(6);
    liste.push_back(0);
    
    //Vorher
    for(list<int>::iterator i= liste.begin(); i != liste.end(); ++i)
      {
      cout << (*i) << endl;
      }
    cout << "-----" << endl;
    
    //loeschen
    for(list<int>::iterator i= liste.begin(); i != liste.end(); ++i)
      {
      cout << "bearbeite -->" << (*i) << "<--" << endl;
      if ( (*i) == 0 )
            liste.erase(i);
      }
    
    cout << "------" << endl;
    /Nachher
    for(list<int>::iterator i= liste.begin(); i != liste.end(); ++i)
      {
      cout << (*i) << endl;
      }
    
    return 0;
    }
    

    Mein Problem ist nun dabei, dass durch das erase() wohl der Iterator ungueltig wird und das Programm stirbt.

    Kann man das Problem irgendwie beheben?

    Dankeschön



  • Hol dir vor dem erase() schon den Iterator auf das nächste Element. 😉



  • war das nicht so, daß man die schon gelesenen löschen darf?

    for(list<int>::iterator i=liste.begin(),next; i != liste.end(); i=next) 
    { 
      next=i;
    
      cout << "bearbeite -->" << (*i) << "<--" << endl; 
      if ( (*i) == 0 ) 
            liste.erase(i); 
    }
    


  • //loeschen
    for(list<int>::iterator i= liste.begin(); i != liste.end(); ++i)
      {
      cout << "bearbeite -->" << (*i) << "<--" << endl;
      if ( (*i) == 0 )
            liste.erase(i++); // so
            i = liste.erase(i); // oder so
      }
    
    cout << "------" << endl;
    


  • Eher so:

    //loeschen
    for(list<int>::iterator i= liste.begin(); i != liste.end(); )
      {
      cout << "bearbeite -->" << (*i) << "<--" << endl;
      list<int>::iterator kill = i;
      ++i; 
      if ( (*kill) == 0 )
            liste.erase(kill); // so
      }
    
    cout << "------" << endl;
    


  • Danke für die vielen und guten Antworten.

    //loeschen
    for(list<int>::iterator i= liste.begin(); i != liste.end(); ++i)  //INC
      {
      cout << "bearbeite -->" << (*i) << "<--" << endl;
      if ( (*i) == 0 )
            i = liste.erase(i); // oder so       //INC
      }
    
    cout << "------" << endl;
    

    Hast diese Loesung nicht den Effekt, dass der Iterator im Fall eines erase() doppelt inkrementiert wird - d.h. einmal im Schleifensteuerkopf (for) und in der Schleife selbst.

    Ist das genau so erforderlich oder werden so Elemente übersprungen?
    Randelementprobleme?

    Ich probiere das gleich mal aus:

    list<int>::iterator i= liste.begin();
    while( i != liste.end() )
      {
      if ( (*i) == 0 )
          i= liste.erase(i);
      else
          ++i;
      }
    


  • Wenn es nach dem Muster abläuft wie schon dir beschrieben kann man auch einen STL Algo nehmen. Also ich meine das

    - Für jeden Eintrag in der Liste
    |-- Wenn Bedingung X erfüllt
      |-- Lösche aktuellen Eintrag
    

    Das geht dann so:

    #include <list>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int main()
    {
    	std::list<int> list;
    
    	/* Liste mit Daten füllen ... */
    
    	list.remove_if(std::bind2nd(std::equal_to<int>(), 0));
    	std::copy(list.begin(), list.end(), std::ostream_iterator<int>(std::cout, " "));
    
    }
    

    Ich bin der Meinung dass man hier die STL nutzen sollte.

    Gruß



  • remove_if löscht aber nicht, sondern verschiebt nur. Da müsste dann noch ein list.erase( i, list.end()) her.



  • 7H3 N4C3R schrieb:

    remove_if löscht aber nicht, sondern verschiebt nur. Da müsste dann noch ein list.erase( i, list.end()) her.

    Hör auf mich zu verunsichern...

    The C++ Standard Library schrieb:

    Operation - Effect
    c.remove_if(op) - Removes all elements for which op(elem) yields true

    Warum sollte das Elemente nicht entfernen? Und wo sollten sie hingehen? 😮 🙄
    Der Unterschied zwischen erase / remove(_if) ist dass erase einen Iterator will und remove(_if) ein Value.

    Gruß



  • Aber nicht nur die halbe Geschichte erzählen 😉 :

    Remove_if removes from the range [first, last) every element x such that pred(x) is true. That is, remove_if returns an iterator new_last such that the range [first, new_last) contains no elements for which pred is true. [1] The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove_if is stable, meaning that the relative order of elements that are not removed is unchanged.

    Ich hoffe das macht deutlich, dass die Elemente im Container immernoch da sind.

    Beispielcode aus der SGI-Doku, wo du dein Zitat wohl auch her hast:

    copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
        // The output is "1 4 2 8 5 7"
    
    vector<int>::iterator new_end = 
            remove_if(V.begin(), V.end(), 
                      compose1(bind2nd(equal_to<int>(), 0),
                               bind2nd(modulus<int>(), 2)));
    V.erase(new_end, V.end()); [1]
    
    copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
        // The output is "1 5 7".
    

    Gruß



  • Nana ich rede von std::list::remove_if und nicht von std::remove_if, das ist schon ein Unterschied, vor allem da std::list::remove_if überhauptkein Rückgabewert hat. Ich bin mir nicht sicher ob das Verhalten von std::remove_if auf eine std::list definiert ist.

    Edit: Achja ich hab in The C++ Standard Library - A Tutorial and Reference von Nicolai M. Josuttis.

    Gruß



  • Argh, verguckt. 😮 🤡
    Okay dann hast Du natürlich recht.


Anmelden zum Antworten