Las reglas para la invalidación del iterador son específicas de un contenedor.
Ahora invalidación puede tener 2 significados con un vector:
- Invalidación = punto fuera del rango definido por [comenzar, terminar]
- Invalidación = punto a un objeto diferente de la de origen
Como se puede ver, el segundo es mucho más estricta:
std::vector<int> myVector;
myVector.push_back(0);
myVector.push_back(1);
std::vector<int>::iterator it = myVector.begin(); // it points to 0
myVector.erase(it); // it points to 1
myVector.erase(it); // it == myVector.end()
En este caso, es 'válido' porque siempre está en el rango inclusivo [inicio, fin] y, por lo tanto, se puede usar con seguridad para cualquier operación en myVector. Por otro lado, la expresión (* it) sigue cambiando: primero devuelve 0, luego 1, luego tiene un comportamiento indefinido ...
En general, las personas prefieren hablar sobre el segundo requisito e invalidar un iterador simplemente significa que (* it) puede no producir el mismo resultado que antes.
Ahora que esto se dice, hay varias maneras para invalidar un iterador en un vector (de hecho, es la estructura más inestable del STL).
Durante adiciones de elementos:
- Esto puede desencadenar una reasignación (1) si myVector.size() == myVector.capacity(), ya que la comprobación es propenso a errores, por lo general debe tener en cuenta que cualquier adición invalidará los iteradores
- Si quiere ser 'quisquilloso' y sabe que la reasignación no se activa, entonces todavía tiene que preocuparse por
insert
. La inserción de un elemento invalida los iteradores que apuntan a esta posición actual y todas las subsecuentes, ya que los elementos se desplazan un paso hacia el final del vector.
Durante la eliminación de elementos:
- No hay ninguna reasignación, incluso si el búfer es ahora mucho más grande de lo necesario. Sin embargo, es posible forzar esto, utilizando la contracción para ajustarse al idioma (2).
- Todos los iteradores que apuntan más allá del elemento eliminado se invalidan. Especialmente, el iterador 'final' anterior ahora está más allá del rango [inicio, final] y no puede usarse de manera segura dentro de los algoritmos AWL, por ejemplo.
(1) La estructura interna de un std :: vector es un vector de T, esto es debido para la compatibilidad con la C-programas (usando & myVector.front() como la dirección de la matriz) y porque garantiza la contigüidad y una sobrecarga espacial mínima (es decir, la cantidad de espacio tomada por el vector de datos propios frente a la cantidad de espacio ocupado por un objeto)
En cualquier momento, puede saber cuántos objetos puede contener un vector usando el método .capacity().
Cuando desea insertar un objeto y el vector no tiene la capacidad necesaria, se desencadena una llamada al método .reserve (size_t). Este método, si el número de elementos requeridos es superior a la capacidad actual, desencadena una reasignación .
El vector luego asigna una nueva matriz de elementos (su tamaño es generalmente 2 * n + 1 donde n es la capacidad actual), copia los elementos de la matriz actual en la nueva matriz, descarta la matriz actual.
Como descarta la matriz actual, sus iteradores se invalidan ya que los iteradores de vectores generalmente son simples punteros (para mayor eficiencia).
Tenga en cuenta que si los iteradores se implementan como: una referencia al vector + un recuento, y eliminación de referencias era en realidad * (& m_vector.front() + n) reasignación no invalidaría ellos ... pero que sería menos eficiente.
(2) Contraer para ajustar: Advertencia, esto desencadena una COPIA de los elementos e invalida los iteradores.
// myVector has 10 elements, but myVector.capacity() == 1000
myVector.swap(std::vector<int>(myVector));
Se crea primero un vector temporal, que destinará únicamente la cantidad de memoria que se necesita (con un mínimo en función de la biblioteca), y copiar los elementos de myVector. Luego, la operación de intercambio intercambia los almacenamientos intermedios de myVector y esta copia, y así myVector ahora almacena un buffer con la cantidad mínima de memoria necesaria. Al final de la operación, se destruye el temporal y se libera la memoria que contiene.
Cuando tengo preguntas como estas, un google rápido puede responderlas. Googleing "std vector push_back" puede llevarlo [aquí] (http://en.cppreference.com/w/cpp/container/vector/push_back), y si lo lee, dice "Si el nuevo tamaño() es mayor que la capacidad(), entonces todos los iteradores y referencias (incluido el iterador pasado) se invalidan. De lo contrario, solo se invalida el iterador pasado-al-fin ". – Cornstalks