Ich habe einen Vektor von IInventory * und durchlaufe die Liste mit dem C ++ 11-Bereich für, um mit jedem etwas zu tun.
Nachdem ich einige Dinge mit einem gemacht habe, möchte ich es vielleicht aus der Liste entfernen und das Objekt löschen. Ich weiß, dass ich delete
den Zeiger jederzeit aufrufen kann, um ihn zu bereinigen, aber wie kann ich ihn in der Bereichsschleife ordnungsgemäß aus dem Vektor entfernen for
? Und wenn ich es aus der Liste entferne, wird meine Schleife ungültig?
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
for (IInventory* index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
}
std::remove_if
ein Prädikat verwenden, das "Sachen macht" und dann true zurückgibt, wenn Sie das Element entfernen möchten.std::list
unten zuAntworten:
Nein, das kannst du nicht. Bereichsbasiert
for
ist, wenn Sie einmal auf jedes Element eines Containers zugreifen müssen.Sie sollten die normale
for
Schleife oder einen ihrer Cousins verwenden, wenn Sie den Container im Laufe der Zeit ändern, mehrmals auf ein Element zugreifen oder auf andere Weise nichtlinear durch den Container iterieren müssen.Beispielsweise:
quelle
true
AFAIU zurückgibt, und es auf diese Weise besser erscheint, die Iterationslogik nicht mit dem Prädikat zu mischen.remove_if
ist besser.erase
gibt einen neuen gültigen Iterator zurück. Es ist vielleicht nicht effizient, aber es funktioniert garantiert.Jedes Mal, wenn ein Element aus dem Vektor entfernt wird, müssen Sie davon ausgehen, dass die Iteratoren am oder nach dem gelöschten Element nicht mehr gültig sind, da jedes der Elemente, die auf das gelöschte Element folgen, verschoben wird.
Eine bereichsbasierte for-Schleife ist nur syntaktischer Zucker für eine "normale" Schleife, die Iteratoren verwendet. Daher gilt das Obige.
Davon abgesehen könnten Sie einfach:
quelle
vector
wird aufgrund eines Aufrufs von niemals neu zugewiesenerase
. Der Grund, warum die Iteratoren ungültig werden, liegt darin, dass jedes der Elemente, die auf das gelöschte Element folgen, verschoben wird.[&]
wäre angemessen, damit er mit lokalen Variablen "ein paar Sachen machen" kann.remove_if
mit.erase
, sonst passiert nichts.std::remove_if
ist O (n).remove_if
intern erfolgen muss). Wenn Sie jedoch eine habenvector
aus 5 Elementen und Sie nur.erase()
1 zu einer Zeit, gibt es keine Auswirkungen auf die Leistung für die Verwendung von Iteratoren vsremove_if
. Wenn die Liste größer ist, sollten Sie wirklich zu einem Ort wechseln,std::list
an dem viele Listen in der Mitte der Liste entfernt werden.Idealerweise sollten Sie den Vektor nicht ändern, während Sie darüber iterieren. Verwenden Sie die Lösch-Entfernungs-Sprache. Wenn Sie dies tun, werden Sie wahrscheinlich auf einige Probleme stoßen. Da in a
vector
anerase
alle Iteratoren ungültig werden, beginnend mit dem zu löschenden Element bis zum, müssenend()
Sie sicherstellen, dass Ihre Iteratoren gültig bleiben, indem Sie Folgendes verwenden:Beachten Sie, dass Sie den
b != v.end()
Test unverändert benötigen . Wenn Sie versuchen, es wie folgt zu optimieren:Sie werden auf UB stoßen, da Ihre
e
nach dem erstenerase
Anruf ungültig ist .quelle
std::remove
und es ist O (N ^ 2), nicht O (N).Ist es eine strikte Anforderung, Elemente in dieser Schleife zu entfernen? Andernfalls können Sie die zu löschenden Zeiger auf NULL setzen und den Vektor erneut durchlaufen, um alle NULL-Zeiger zu entfernen.
quelle
Es tut mir leid für das Nekroposting und auch, wenn meine C ++ - Expertise meiner Antwort im Wege steht. Wenn Sie jedoch versuchen, jedes Element zu durchlaufen und mögliche Änderungen vorzunehmen (z. B. das Löschen eines Index), versuchen Sie, eine Backwords for-Schleife zu verwenden.
Beim Löschen des Index x befindet sich die nächste Schleife für das Element "vor" der letzten Iteration. Ich hoffe wirklich, dass dies jemandem geholfen hat
quelle
OK, ich bin spät dran, aber trotzdem: Entschuldigung, nicht richtig, was ich bisher gelesen habe - es ist möglich, dass Sie nur zwei Iteratoren benötigen:
Nur das Ändern des Werts, auf den ein Iterator zeigt, macht keinen anderen Iterator ungültig, sodass wir dies tun können, ohne uns Sorgen machen zu müssen. Tatsächlich macht
std::remove_if
(zumindest die gcc-Implementierung) etwas sehr Ähnliches (unter Verwendung einer klassischen Schleife ...), löscht einfach nichts und löscht nicht.Beachten Sie jedoch, dass dies nicht threadsicher (!) Ist - dies gilt jedoch auch für einige der anderen oben genannten Lösungen ...
quelle
erase
löschen würden (vorausgesetzt, Sie löschen natürlich mehr als ein einzelnes Element)?Ich werde anhand eines Beispiels zeigen, wie das folgende Beispiel ungerade Elemente aus dem Vektor entfernt:
Ausgabe aw unten:
Beachten Sie, dass die Methode
erase
den nächsten Iterator des übergebenen Iterators zurückgibt.Von hier aus können wir eine generiertere Methode verwenden:
Sehen Sie hier, um zu sehen, wie man es benutzt
std::remove_if
. https://en.cppreference.com/w/cpp/algorithm/removequelle
Im Gegensatz zu diesem Thread-Titel würde ich zwei Durchgänge verwenden:
quelle
Eine viel elegantere Lösung wäre der Wechsel zu
std::list
(vorausgesetzt, Sie benötigen keinen schnellen Direktzugriff).Sie können dann mit
.remove_if
und einem C ++ - Funktor in einer Zeile löschen :Also schreibe ich hier nur einen Funktor, der ein Argument (das
Widget*
) akzeptiert . Der Rückgabewert ist die Bedingung, unter der aWidget*
aus der Liste entfernt werden soll.Ich finde diese Syntax schmackhaft. Ich glaube nicht, dass ich jemals
remove_if
für std :: -Vektoren verwenden würde - es gibt so vielinv.begin()
undinv.end()
Rauschen, dass Sie wahrscheinlich besser dran sind, einen Ganzzahlindex-basierten Löschvorgang oder nur einen einfachen alten regulären Iterator-basierten Löschvorgang zu verwenden (wie gezeigt) unten). Aber Sie sollten nicht wirklich aus der Mitte eines entfernenstd::vector
sowieso sehr viel , daherlist
wird empfohlen, für diesen Fall des häufigen Löschens in der Mitte der Liste zu einem zu wechseln .Beachten Sie jedoch, dass ich keine Gelegenheit hatte anzurufen
delete
auf demWidget*
‚s , die entfernt wurden. Um das zu tun, würde es so aussehen:Sie können auch eine reguläre iteratorbasierte Schleife verwenden:
Wenn Sie die Länge von nicht mögen
for( list<Widget*>::iterator iter = widgets.begin() ; ...
, können Sie verwendenquelle
remove_if
an einemstd::vector
tatsächlich funktioniert und wie es die Komplexität auf O (N) hält.std::vector
entfernen, wird jedes Element immer nach dem Element verschoben, das Sie entfernt haben, wodurchstd::list
eine viel bessere Wahl getroffen wird.remove_if
schiebt jedes Element um die Anzahl der freigegebenen Leerzeichen nach oben. Zu der Zeit , erklären Sie Cache - Nutzung,remove_if
auf einestd::vector
wahrscheinlich übertrifft Entfernung von einstd::list
. Und bewahrt denO(1)
wahlfreien Zugriff.Ich denke, ich würde folgendes tun ...
quelle
Sie können den Iterator während der Schleifeniteration nicht löschen, da die Anzahl der Iteratoren nicht übereinstimmt und nach einer gewissen Iteration ein ungültiger Iterator vorliegt.
Lösung: 1) Nehmen Sie die Kopie des Originalvektors. 2) Iterieren Sie den Iterator mit dieser Kopie. 2) Machen Sie einige Dinge und löschen Sie sie aus dem Originalvektor.
quelle
Das Löschen eines Elements nach dem anderen führt leicht zu einer N ^ 2-Leistung. Markieren Sie besser Elemente, die gelöscht werden sollen, und löschen Sie sie sofort nach der Schleife. Wenn ich nullptr in einem ungültigen Element in Ihrem Vektor annehmen darf, dann
sollte arbeiten.
Falls Ihr "Do some stuff" keine Elemente des Vektors ändert und nur dazu dient, die Entscheidung zu treffen, das Element zu entfernen oder beizubehalten, können Sie es in Lambda konvertieren (wie in einem früheren Beitrag von jemandem vorgeschlagen) und verwenden
quelle