Erreurs fréquentes avec les itérateurs des conteneurs STL en C++

Lors de l'utilisation des conteneurs STL, la fonctino membre erase() peut invalider les itérateurs, menant à des plantages. Considérons un exemple erroné avec une map :

map<int, int> monDico;
// Insertions de données...
for (auto it = monDico.begin(); it != monDico.end(); it++)
    if (it->second % 2 == 1) monDico.erase(it); // Ceci cause une erreur d'exécution.

Pour coriger cela, deux approches sont valides :

Première méthode :

for (auto it = monDico.begin(); it != monDico.end(); it++)
    if (it->second % 2 == 1) it = monDico.erase(it); // Utilise le retour de erase().

Cette méthode repose sur la valeur de retour de erase(), qui renvoie un itérateur vers l'élément suivant. Elle est disponible en C++11 et versions ultérieures. Dans les versions antérieures, erase() ne renvoyait rien, comme illustré dans l'implémentation conditionnelle :

#if __cplusplus >= 201103L
      iterator erase(const_iterator __position) { return _M_t.erase(__position); }
#else
      void erase(iterator __position) { _M_t.erase(__position); }
#endif

Deuxième méthode :

for (auto it = monDico.begin(); it != monDico.end(); it++)
    if (it->second % 2 == 1) monDico.erase(it++); // Incrémentation avant suppression.

Cette technique fonctionne indépendamment de la version de C++.

Pour supprimer une plage d'éléments, une fonction membre dédiée existe : erase(const_iterator first, const_iterator last).

Concernant la modification via itérateurs, dans les conteneurs séquentiels comme vector, les éléments peuvent être changés directement :

vector<int> donnees = {1, 2, 3, 4, 5};
for (auto iter = donnees.begin(); iter != donnees.end(); iter++)
    cout << *iter << " ";
*--iter = 1; // Modifie le dernier élément.
cout << endl;
for (auto iter = donnees.begin(); iter != donnees.end(); iter++)
    cout << *iter << " ";

La sortie est : 1 2 3 4 5 puis 1 2 3 4 1.

Pour les conteneurs asssociatifs comme map, la clé est en lecture seule pour préserver l'ordre des éléments :

map<int, int> annuaire;
annuaire[1] = 2;
annuaire[2] = 3;
for (auto it = annuaire.begin(); it != annuaire.end(); it++)
    cout << it->first << " " << it->second << endl;
(--it)->first = 3; // Erreur : membre en lecture seule.

Pour modifier une clé, il faut supprimer l'élément et en réinsérer un nouveau :

annuaire.erase(--it);
annuaire.insert(make_pair(3, 3));

Cela est particulièrement utile pour les ensembles (set). Notez que dans map, la valeur associée (second membre) peut être modifiée librement.

Étiquettes: C++ STL itérateurs Conteneurs map

Publié le 5 juin à 02h10