web-dev-qa-db-fra.com

Problème avec std :: map :: iterator après avoir appelé erase ()

// erasing from map
#include <iostream>
#include <map>
using namespace std;

int main ()
{
  map<char,int> mymap;
  map<char,int>::iterator it(mymap.begin());

  // insert some values:
  mymap['a']=10;
  mymap['b']=20;
  mymap['c']=30;
  mymap['d']=40;
  mymap['e']=50;
  mymap['f']=60;

  it=mymap.find('a');
  mymap.erase (it);                   // erasing by iterator

  // show content:
  for (; it != mymap.end(); it++ )
    cout << (*it).first << " => " << (*it).second << endl;
  return 0;
}

Pourquoi cela donne-t-il une sortie comme 

a => 10
b => 20
c => 30
d => 40
e => 50
f => 60

de toute façon, "a => 10" ne devrait pas être supprimé, mais si je déclare it = mymap.begin() dans la boucle for, tout est parfait. Pourquoi?

programme adapté de: http://www.cplusplus.com/reference/stl/map/erase/

17
Sunny Raj

Effacer un élément de map invalide les itérateurs pointant sur cet élément (une fois que tout cet élément a été supprimé). Vous ne devriez pas réutiliser cet itérateur.

Depuis C++ 11, erase() renvoie un nouvel itérateur pointant sur l'élément suivant, qui peut être utilisé pour continuer à itérer:

it = mymap.begin();
while (it != mymap.end()) {
   if (something)
      it = mymap.erase(it);
   else
      it++;
}

Avant C++ 11, vous deviez faire avancer l'itérateur manuellement vers l'élément suivant avant que la suppression ait lieu, par exemple comme ceci:

mymap.erase(it++);

Cela fonctionne car l'effet secondaire de it++ après l'incrémentation se produit avant que erase() ne supprime l'élément. Comme cela n’est peut-être pas immédiatement évident, la variante C++ 11 ci-dessus devrait être préférée.

38
sth

L'appel de erase() invalide l'itérateur. Dans ce cas, l'itérateur pointe la valeur résiduelle laissée dans la mémoire (mais ne vous fiez pas à ce comportement indéfini!). Réinitialisez l'itérateur avec it=mymap.begin() avant la boucle pour obtenir les résultats souhaités.

http://codepad.org/zVFRtoV5

Cette réponse montre comment effacer des éléments en effectuant une itération sur un std::map:

for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
    // wilhelmtell in the comments is right: no need to check for NULL. 
    // delete of a NULL pointer is a no-op.
    if(it->second != NULL) {
        delete it->second;
            it->second = NULL;
    }
}
3
marcog

Cela a à voir avec la façon dont la map est implémentée. Disons que c'est un arbre, comme:

class map_node {
    char key;
    int  value;
    map_node* next;
    ...
};

Lorsque vous erase() l'itérateur, vous supprimez le nœud de l'arborescence et libérez son espace. Mais jusqu'à ce que cet emplacement de mémoire soit écrasé, le contenu du nœud est toujours en mémoire. C'est pourquoi vous pouvez obtenir non seulement la valeur, mais également l'élément suivant de l'arbre. Ainsi, votre résultat est tout à fait attendu.

1
chrisaycock

it n'est plus valide après mymap.erase(it). Cela signifie qu'il peut faire ce qu'il veut.

0
Oswald

"it" pointe toujours au même endroit, effacer ne met pas à jour l'itérateur par lui-même, vous devez le faire en réinitialisant l'itérateur. En effet, "it" pointe sur l’ancien emplacement qui a été effacé du vecteur mais contient toujours les anciennes données.

0
Akhil