2009-06-19 4 views
14

Ho un iteratore di lista che passa attraverso un elenco e rimuove tutti i numeri pari. Posso usare l'elenco iteratore per stampare i numeri, ma non posso usare la lista remove() e passare l'iteratore dereferenziato.Lista Iterator Remove()

Ho notato che quando l'istruzione remove() è attiva, * itr viene danneggiato? Qualcuno può spiegarlo?

#include <iostream> 
#include <list> 

#define MAX 100 

using namespace std; 

int main() 
{ 
    list<int> listA; 
    list<int>::iterator itr; 

    //create list of 0 to 100 
    for(int i=0; i<=MAX; i++) 
     listA.push_back(i); 

    //remove even numbers 
    for(itr = listA.begin(); itr != listA.end(); ++itr) 
    { 
     if (*itr % 2 == 0) 
     { 
      cout << *itr << endl; 
      listA.remove(*itr); //comment this line out and it will print properly 
     } 
    } 
} 

risposta

41

Ci sono alcuni problemi con il tuo codice qui sopra. Innanzitutto, lo remove invaliderà tutti gli iteratori che puntano agli elementi rimossi. Continui quindi a continuare a usare l'iteratore. È difficile stabilire quale elemento (i) remove si cancelli nel caso generale (anche se non nel tuo) poiché può rimuovere più di uno.

In secondo luogo, si sta probabilmente utilizzando il metodo sbagliato. Remove itererà attraverso tutti gli elementi nell'elenco alla ricerca di eventuali elementi di corrispondenza - questo sarà inefficiente nel tuo caso perché ce n'è solo uno. Sembra che dovresti usare il metodo erase, probabilmente vuoi solo cancellare l'oggetto nella posizione dell'iteratore. La cosa buona di erase è che restituisce un iteratore che si trova nella successiva posizione valida. Il modo idiomatico di usarlo è qualcosa di simile:

//remove even numbers 
for(itr = listA.begin(); itr != listA.end();) 
{ 
    if (*itr % 2 == 0) 
    { 
     cout << *itr << endl; 
     itr=listA.erase(itr); 
    } 
    else 
     ++itr; 
} 

Infine, si potrebbe anche usare remove_if a fare lo stesso, come si sta facendo:

bool even(int i) { return i % 2 == 0; } 

listA.remove_if(even); 
2

Non è possibile utilizzare un iteratore dopo aver eliminato l'elemento a cui si riferisce.

Tuttavia, gli elenchi iteratori che fanno riferimento a elementi non eliminati dopo un remove() devono rimanere validi.

-1

Poiché gli iteratori dipendono dalla lunghezza della struttura rimanente, la maggior parte degli iteratori non consente la modifica di un elenco mentre l'iteratore è in uso. Se vuoi passare e modificare l'elenco, dovrai utilizzare un ciclo indipendente dall'iteratore.

+4

Devo sottolineare che gli iteratori STL non hanno alcuna dipendenza dalla lunghezza della struttura. Gli iteratori ti consentono spesso di eliminare determinati elementi, ad esempio gli iteratori del vettore ti consentono di eliminare gli elementi oltre l'iteratore e gli iteratori di elenchi ti consentono di eliminare tutto ciò che non è indicato dall'iteratore. –

0

Potremmo usare qualcosa di simile a questo:

container.erase(it++); 

ho provato su questo esempio:

int main(){ 

list<int>*a=new list<int>; 
a->push_back(1); 
a->push_back(2); 
a->push_back(3); 

list<int>::iterator I; 

I=a->begin(); ++I; 

a->erase(I++); 
cout<<*I<<endl; 
} 

e visualizzato 3, come volevo. Ora non so se questo è valido o uno di quelli che "a volte funzionano e talvolta no".

EDIT: Forse è a causa del compilatore. Ad esempio, il compilatore che sto usando (GNU gcc-g ++) sta trattando liste (std: :) come circolare, cioè se aumento iteratore dopo list-> end() ti mette all'inizio.

+0

Funzionerà con gli iteratori 'std :: list' perché quegli iteratori sono invalidati solo quando l'elemento a cui stanno puntando viene cancellato. Ma questo non funzionerà con gli iteratori 'std :: vector' perché quegli iteratori sono invalidati quando l'elemento a cui puntano o qualsiasi elemento prima di quello a cui stanno puntando viene cancellato. – David