Los iteradores son objetos que permiten referirse a los elementos de los contenedores STL de manera similar a los punteros. Proporcionan un método unificado de acceso a los elementos de cualquier contenedor (vector, lista, mapa, etc.). Los iteradores vienen en varios tipos:
Se debe prestar especial atención a la vida de los iteradores:
Ejemplo:
std::vector<int> v = {1,2,3,4,5}; for (auto it = v.begin(); it != v.end(); ++it) { if (*it == 3) { // Eliminamos el elemento con valor 3 it = v.erase(it); // erase devuelve el iterador al siguiente elemento --it; // ajustar el iterador si es necesario } }
Pregunta: ¿Con la llamada a std::vector::insert se invalidan los iteradores?
Respuesta común: No, solo al agregar fuera del rango final.
Respuesta correcta: Todos los iteradores y referencias que son iguales o siguen a la posición de inserción se invalidan si la capacidad del contenedor aumenta. Si la capacidad es suficiente, solo se invalidan los iteradores en el rango después del punto de inserción.
Ejemplo:
std::vector<int> v = {1,2,3}; auto it = v.begin() + 1; v.insert(v.begin(), 0); // it aquí puede estar invalidado!
Historia: En un proyecto, se utilizaron punteros para iterar sobre std::vector y, después de un push_back, la iteración continuaba sobre punteros invalidados, lo que causando un fallo en la aplicación.
Historia: Un desarrollador eliminaba elementos de std::list usando erase en un bucle for(auto it : list), sin usar el iterador devuelto por erase, como resultado, la iteración saltaba elementos y no se eliminaban todos los necesarios.
Historia: En el código se utilizaba std::map, y después de erase por clave, el iterador permanecía vinculado al elemento eliminado (comportamiento indefinido) — esto provocó fallos aleatorios en posteriores accesos.