2009-06-19 7 views
14

J'ai un itérateur de liste qui parcourt une liste et supprime tous les nombres pairs. Je peux utiliser l'itérateur de liste pour imprimer les nombres, mais je ne peux pas utiliser la liste remove() et passer dans l'itérateur déréférencé. J'ai remarqué que lorsque l'instruction remove() est en vigueur, * itr est corrompu? Quelqu'un peut-il expliquer cela?Liste Iterator Remove()

#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 
     } 
    } 
} 

Répondre

41

Il y a quelques problèmes avec votre code ci-dessus. Premièrement, le remove invalidera les itérateurs pointant sur les éléments supprimés. Vous continuez ensuite à continuer à utiliser l'itérateur. Il est difficile de dire quel (s) élément (s) remove effacerait dans le cas général (mais pas dans le vôtre) puisqu'il peut en supprimer plus d'un.

Deuxièmement, vous utilisez probablement la mauvaise méthode. Supprimer parcourra tous les éléments de la liste à la recherche d'éléments correspondants - ce sera inefficace dans votre cas car il n'y en a qu'un seul. Il semble que vous devriez utiliser la méthode erase, vous voulez probablement seulement effacer l'élément à la position de l'itérateur. La bonne chose à propos de erase est qu'il renvoie un itérateur qui est à la prochaine position valide. La façon idiomatiques de l'utiliser est quelque chose comme ceci:

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

Enfin, vous pouvez également utiliser remove_if pour faire la même chose que vous faites:

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

listA.remove_if(even); 
2

Vous ne pouvez pas utiliser un itérateur après avoir supprimé l'élément auquel il faisait référence. Toutefois, la liste des itérateurs qui font référence à des éléments non supprimés après remove() doit rester valide.

-1

Puisque les itérateurs dépendent de la longueur de la structure restant la même, la plupart des itérateurs ne permettent pas de changer une liste pendant que l'itérateur est utilisé. Si vous voulez passer en revue et modifier la liste, vous devrez utiliser une boucle indépendante de l'itérateur.

+4

Je dois souligner que les itérateurs STL ne dépendent pas de la longueur de la structure. Les itérateurs vous permettent souvent de supprimer certains éléments, par exemple les itérateurs vectoriels vous permettent de supprimer des éléments après l'itérateur, et les itérateurs de liste vous permettent de supprimer tout ce qui n'est pas pointé par l'itérateur. –

0

Peut-on utiliser quelque chose comme ceci:

container.erase(it++); 

J'ai essayé sur cet exemple:

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; 
} 

et il en a affiché 3, comme je le voulais. Maintenant, je ne sais pas si cela est valable ou l'un de ceux qui "travaillent parfois et parfois non".

EDIT: Peut-être que c'est à cause du compilateur. Par exemple, le compilateur que j'utilise (GNU gcc-g ++) traite les listes (std: :) comme des circulaires, c'est-à-dire si j'augmente itérateur après list-> end() cela vous met au début.

+0

Cela fonctionnera pour les itérateurs 'std :: list' car ces itérateurs ne sont invalidés que lorsque l'élément sur lequel ils pointent est supprimé. Mais cela ne fonctionnera pas pour les itérateurs 'std :: vector' parce que ces itérateurs sont invalidés quand l'élément vers lequel ils pointent ou tout élément avant celui sur lequel ils pointent est supprimé. – David

Questions connexes