Ich habe Code, der so aussieht:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
Ich möchte inaktive Elemente sofort nach dem Aktualisieren entfernen, um ein erneutes Durchlaufen der Liste zu vermeiden. Wenn ich jedoch die auskommentierten Zeilen hinzufüge, wird folgende Fehlermeldung i++
angezeigt: "Listeniterator nicht inkrementierbar". Ich habe einige Alternativen ausprobiert, die die for-Anweisung nicht erhöht haben, aber ich konnte nichts zum Laufen bringen.
Was ist der beste Weg, um Elemente zu entfernen, während Sie eine std :: -Liste durchlaufen?
Antworten:
Sie müssen zuerst den Iterator inkrementieren (mit i ++) und dann das vorherige Element entfernen (z. B. indem Sie den von i ++ zurückgegebenen Wert verwenden). Sie können den Code wie folgt in eine while-Schleife ändern:
quelle
i = items.erase(i)
ist sicherer, da sie für eine Liste gleichwertig ist, aber trotzdem funktioniert, wenn jemand den Container in einen Vektor ändert. Mit einem Vektor verschiebt erase () alles nach links, um das Loch zu füllen. Wenn Sie versuchen, das letzte Element mit Code zu entfernen, der den Iterator nach dem Löschen inkrementiert, wird das Ende nach links und der Iterator nach rechts verschoben - nach dem Ende. Und dann stürzt du ab.Du willst machen:
Dadurch wird der Iterator korrekt aktualisiert und zeigt auf den Speicherort nach dem entfernten Iterator.
quelle
i==items.begin()
?i= items.erase(i);
. Es ist die kanonische Form und kümmert sich bereits um all diese Details.Sie müssen die Kombination aus Kristos Antwort und MSNs ausführen:
Das effizienteste und SuperCool® STL-versierte Ding wäre natürlich ungefähr so:
quelle
Verwenden Sie den Algorithmus std :: remove_if.
Bearbeiten: Die Arbeit mit Sammlungen sollte wie folgt aussehen: 1. Sammlung vorbereiten. 2. Prozesssammlung.
Das Leben wird einfacher, wenn Sie diese Schritte nicht mischen.
quelle
Hier ist ein Beispiel mit einer
for
Schleife, die die Liste iteriert und den Iterator erhöht oder erneut validiert, falls ein Element während des Durchlaufs der Liste entfernt wird.quelle
Die Alternative zur Loop-Version zu Kristos Antwort.
Sie verlieren etwas an Effizienz, gehen beim Löschen vorwärts und dann wieder vorwärts, aber im Austausch für das zusätzliche Iteratorinkrement können Sie den Iterator im Schleifenbereich deklarieren lassen und den Code etwas sauberer aussehen lassen. Was zu wählen ist, hängt von den Prioritäten des Augenblicks ab.
Die Antwort war völlig verspätet, ich weiß ...
quelle
iterator cannot be decremented
Dieerase
Methode benötigt arandom access iterator
. Einige Sammlungsimplementierungen bieten eine,forward only iterator
die die Behauptung verursacht.Ich habe es zusammengefasst, hier sind die drei Methoden mit Beispiel:
1. mit
while
Schleife2. Verwenden der
remove_if
Mitgliederfunktion in der Liste:3. Verwenden der
std::remove_if
Funktion in Kombination mit der Elementfunktionerase
:4. Verwenden Sie die
for
Schleife, und beachten Sie, dass der Iterator aktualisiert wird:quelle
Durch das Entfernen werden nur die Iteratoren ungültig, die auf die entfernten Elemente verweisen.
In diesem Fall ist i nach dem Entfernen von * i ungültig und Sie können es nicht inkrementieren.
Sie können zuerst den Iterator des Elements speichern, das entfernt werden soll, dann den Iterator erhöhen und dann den gespeicherten entfernen.
quelle
Wenn Sie sich
std::list
eine Warteschlange vorstellen, können Sie alle Elemente, die Sie behalten möchten, aus der Warteschlange entfernen und in die Warteschlange stellen, aber nur das Element, das Sie entfernen möchten, aus der Warteschlange entfernen (und nicht in die Warteschlange stellen). Hier ist ein Beispiel, in dem ich 5 aus einer Liste mit den Nummern 1-10 entfernen möchte ...myList
wird jetzt nur die Nummern 1-4 und 6-10 haben.quelle
Durch das Rückwärtslaufen wird vermieden, dass ein Element auf die verbleibenden zu durchlaufenden Elemente gelöscht wird:
PS: Siehe dies z. B. in Bezug auf die Rückwärtsiteration.
PS2: Ich habe nicht gründlich getestet, ob es gut löschende Elemente an den Enden handhabt.
quelle
avoids the effect of erasing an element on the remaining elements
für eine Liste wahrscheinlich ja. Für einen Vektor vielleicht nicht. Das ist bei beliebigen Sammlungen nicht garantiert. Zum Beispiel kann eine Karte kann entscheiden , sich neu zu gewichten.Du kannst schreiben
Sie können äquivalenten Code mit schreiben
std::list::remove_if
, der weniger ausführlich und expliziter istDas
std::vector::erase
std::remove_if
Idiom sollte verwendet werden, wenn Elemente ein Vektor anstelle einer Liste sind, um die Komplexität bei O (n) zu halten - oder wenn Sie generischen Code schreiben und Elemente möglicherweise ein Container sind, in dem einzelne Elemente (wie ein Vektor) nicht effektiv gelöscht werden können.quelle
Ich denke du hast dort einen Fehler, ich codiere so:
quelle