2011-01-17 18 views
33

Ich habe einen Vektor, der Elemente enthält, die entweder aktiv oder inaktiv sind. Ich möchte, dass die Größe dieses Vektors bei Leistungsproblemen klein bleibt. Daher möchte ich, dass Elemente, die als inaktiv markiert wurden, aus dem Vektor gelöscht werden. Ich habe versucht, dies während der Iteration zu tun, aber ich bekomme den Fehler "Vector Iteratoren inkompatibel".Element aus Vektor während der Iteration entfernen?

vector<Orb>::iterator i = orbsList.begin(); 

    while(i != orbsList.end()) { 
     bool isActive = (*i).active; 

     if(!isActive) { 
      orbsList.erase(i++); 
     } 
     else { 
      // do something with *i 
      ++i; 
     } 
    } 
+0

diese Frage im Zusammenhang Siehe: http://stackoverflow.com/questions/347441/erasing-elements-from-a-vector – Naveen

Antwort

2

Entfernen von Objekten aus der Mitte eines Vektors alle Iteratoren zu diesem Vektor ungültig machen, so dass Sie das nicht tun (Update: ohne zu WILX Vorschlag zurückgreifen).

Wenn Sie sich Sorgen um die Leistung machen, ist das Löschen von Elementen aus der Mitte eines Vektors sowieso eine schlechte Idee. Vielleicht möchten Sie eine std::list verwenden?

+3

Ich weiß, es ist ein wenig spät, aber vielleicht möchten Sie dies lesen. Es ist im Gegensatz zu dem, was die meisten Leute erwarten würden, aber es kommt vom Schöpfer von C++ http://bulldozer00.com/2012/02/09/vectors-and-lists/ – EFreak

13

Sie können das tun, aber Sie müssen Ihre while() ein wenig mischen, denke ich. Die erase()-Funktion gibt einen Iterator für das nächste Element nach dem gelöschten zurück: iterator erase(iterator position);. Zitiert aus dem Standard von 23.1.1/7:

Der Iterator aus a.erase (q) zurück zeigt auf das Element unmittelbar folgenden q vor dem Element ist gelöscht. Wenn kein solches Element existiert, wird a.end() zurückgegeben.

Vielleicht sollten Sie stattdessen die Erase-remove idiom verwenden.

+0

Auch um den Vektor zu verkleinern (wie es scheint, was die OP möchte tun) Der Tauschtrick kann verwendet werden. Details hier: http: //stackoverflow.com/questions/253157/how-to-downsize-stdvector – Naveen

+1

Es könnte genug sein zu sagen "i = orbsList.erase (i)" anstelle von "orbsList.erase (i ++)" – jakebman

1

Wie bereits erwähnt, werden die Iteratoren von Vektor unter vector::erase() ungültig gemacht, unabhängig davon, welche Form des Iteratorinkrements Sie verwenden. Verwenden Sie stattdessen einen ganzzahligen Index.

+2

Mit dem Rückgabewert von Erase erhalten Sie einen gültigen Iterator zurück.Es ist also nur notwendig, den zurückgegebenen Wert dem Iterator zuzuordnen - hier kein Leistungsproblem. – harper

+0

@harper: Eigentlich gibt es hier ein großes Leistungsproblem; Das Löschen eines Elements aus der Mitte eines Vektors erfordert, dass alle anderen Elemente nach unten verschoben werden, was jedes Mal O (N) Konstruktor- und Destruktoraufrufe erfordert. –

+0

Und die Verwendung eines Index hat was genau? Wie wird die Löschleistung erhöht? Ich habe nur die Aussage "Iteratoren werden ungültig gemacht .. egal welche Form" kommentiert ... – harper

2

Sie könnten die Verwendung einer std::list anstelle einer std::vector für Ihre Datenstruktur in Betracht ziehen. Es ist sicherer (weniger fehleranfällig) zu verwenden, wenn Löschen mit Iteration kombiniert wird.

46

Der am besten lesbare Weg, den ich in der Vergangenheit gemacht habe, ist die Verwendung von std::vector::erase kombiniert mit std::remove_if. Im folgenden Beispiel verwende ich diese Kombination, um eine Zahl von weniger als 10 aus einem Vektor zu entfernen.

(Für Nicht-C++ 0x, können Sie das Lambda unten mit Ihrem eigenen Prädikat ersetzen Sie einfach:)

// a list of ints 
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29}; 
std::vector v(myInts, myInts + sizeof(myInts)/sizeof(int)); 

// get rid of anything < 10 
v.erase(std::remove_if(v.begin(), v.end(), 
         [](int i) { return i < 10; }), v.end()); 
+0

Elegant und effektiv! –

+1

Während im einfachen Fall elegant, bin ich seiner Wirksamkeit in der realen Welt zweifelhaft. Dies würde bei einem dynamischen Größenvektor gar nicht funktionieren. –

+2

"Das würde überhaupt nicht mit einem dynamischen Größenvektor funktionieren." Warum nicht? – jhasse

33

ich mit WILX Antwort zustimmen. Hier ist eine Implementierung:

// curFiles is: vector <string> curFiles; 

vector<string>::iterator it = curFiles.begin(); 

while(it != curFiles.end()) { 

    if(aConditionIsMet) { 

     it = curFiles.erase(it); 
    } 
    else ++it; 
} 
4

Wenn jemand auf Indizes müssen arbeiten

vector<int> vector; 
for(int i=0;i<10;++i)vector.push_back(i); 

int size = vector.size(); 
for (int i = 0; i < size; ++i) 
{ 
    assert(i > -1 && i < (int)vector.size()); 
    if(vector[i] % 3 == 0) 
    { 
     printf("Removing %d, %d\n",vector[i],i); 
     vector.erase(vector.begin() + i); 
    } 

    if (size != (int)vector.size()) 
    { 
     --i; 
     size = vector.size(); 
     printf("Go back %d\n",size); 
    } 
} 
+0

Warum nicht nur Größe-- i-- neben löschen? – user1122069

+1

@ user1122069 meine guten Praktiken verbietet solche Dinge, verstehen viele Leute nicht den Unterschied zwischen '--i' nad' i - ' –

Verwandte Themen