print geeksforgeeks for example advance c++ stl iterator dereference

geeksforgeeks - list iterator c++



Comprobando si un iterador es válido (11)

¿Hay alguna forma de verificar si un iterador (ya sea de un vector, una lista, un deque ...) es (aún) no referenciable, es decir, no se ha invalidado?

He estado usando try - catch , pero ¿hay una forma más directa de hacer esto?

Ejemplo: (que no funciona)

list<int> l; for (i = 1; i<10; i++) { l.push_back(i * 10); } itd = l.begin(); itd++; if (something) { l.erase(itd); } /* now, in other place.. check if itd points to somewhere meaningful */ if (itd != l.end()) { // blablabla }


¿Hay alguna forma de verificar si un iterador (ya sea de un vector, una lista, un deque ...) es (aún) no referenciable, es decir, no se ha invalidado?

No, no hay. En su lugar, debe controlar el acceso al contenedor mientras exista su iterador, por ejemplo:

  • Tu hilo no debe modificar el contenedor (invalidando el iterador) mientras aún esté usando un iterador instanciado para ese contenedor

  • Si existe el riesgo de que otros subprocesos modifiquen el contenedor mientras el subproceso está iterando, para que este subproceso sea seguro, el subproceso debe adquirir algún tipo de bloqueo en el contenedor (de modo que impida que otros subprocesos modifiquen el contenedor mientras está usando un iterador)

Las soluciones alternativas como atrapar una excepción no funcionarán.

Esta es una instancia específica del problema más general, "¿puedo probar / detectar si un puntero es válido?", Cuya respuesta suele ser "no, no puede probarlo: en lugar de eso, debe administrar todas las asignaciones de memoria". y eliminaciones para saber si algún puntero dado sigue siendo válido ".


¿Hay alguna manera de comprobar si un iterador es dereferencable

Sí, con los contenedores de depuración gcc disponibles como extensiones GNU. Para std::list puede usar __gnu_debug::list lugar. El siguiente código se cancelará tan pronto como se intente utilizar un iterador no válido. Como los contenedores de depuración imponen una sobrecarga adicional, están pensados ​​solo para la depuración.

#include <debug/list> int main() { __gnu_debug::list<int> l; for (int i = 1; i < 10; i++) { l.push_back(i * 10); } auto itd = l.begin(); itd++; l.erase(itd); /* now, in other place.. check if itd points to somewhere meaningful */ if (itd != l.end()) { // blablabla } } $ ./a.out /usr/include/c++/7/debug/safe_iterator.h:552: Error: attempt to compare a singular iterator to a past-the-end iterator. Objects involved in the operation: iterator "lhs" @ 0x0x7ffda4c57fc0 { type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator); state = singular; references sequence with type ''std::__debug::list<int, std::allocator<int> >'' @ 0x0x7ffda4c57ff0 } iterator "rhs" @ 0x0x7ffda4c580c0 { type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator); state = past-the-end; references sequence with type ''std::__debug::list<int, std::allocator<int> >'' @ 0x0x7ffda4c57ff0 } Aborted (core dumped)


Como dijo jdehaan, si el iterador no fue invalidado y apunta a un contenedor, puede verificarlo comparándolo con container.end() .

Sin embargo, tenga en cuenta que si el iterador es singular , ya que no se inicializó o quedó inválido después de una operación de mutación en el contenedor (los iteradores del vector se invalidan cuando aumenta la capacidad del vector, por ejemplo) - la única operación que se le permite realizar en él es la asignación. En otras palabras, no puede verificar si un iterador es singular o no.

std::vector<int>::iterator iter = vec.begin(); vec.resize(vec.capacity() + 1); // iter is now singular, you may only perform assignment on it, // there is no way in general to determine whether it is singular or not


El tipo de los parámetros de la función de borrado de cualquier contenedor estándar (como ha enumerado en su pregunta, es decir, si se trata de un vector, una lista, un deque ...) siempre es iterador de este contenedor.

Esta función utiliza el primer iterador dado para excluir del contenedor el elemento al que apunta este iterador e incluso los que siguen. Algunos contenedores borran solo un elemento para un iterador, y otros contenedores borran todos los elementos seguidos por un iterador (incluido el elemento señalado por este iterador) al final del contenedor. Si la función de borrado recibe dos iteradores, entonces los dos elementos señalados por cada iterador se borran del contenedor y todo el resto entre ellos también se borra del contenedor, pero el punto es que cada iterador que se pasa al borrado ¡La función de cualquier contenedor estándar se invalida! Tambien

Cada iterador que apuntaba a algún elemento que ha sido borrado del contenedor se invalida, ¡pero no pasa el final del contenedor!

Esto significa que un iterador que apuntaba a algún elemento que se ha borrado del contenedor no puede compararse con container.end (). Este iterador no es válido y, por lo tanto, no es anulable, es decir, no puede utilizar ni los operadores * ni ->, tampoco es incrementable, es decir, no puede usar el operador ++, y tampoco es decrementable, es decir, no puede usar el operador.

Tampoco es comparable !!! IE, ni siquiera puedes usar los operadores == ni! =

En realidad, no puede utilizar ningún operador que esté declarado y definido en el iterador estándar. No se puede hacer nada con este iterador, como puntero nulo.

Hacer algo con un iterador no válido detiene inmediatamente el programa e incluso hace que el programa se bloquee y aparece una ventana de diálogo de aserción. No hay forma de continuar el programa, sin importar qué opciones elija, en qué botones haga clic. Solo puede terminar el programa y el proceso haciendo clic en el botón Abortar.

No hace nada más con un iterador no válido, a menos que pueda configurarlo al principio del contenedor o simplemente ignorarlo.

Pero antes de decidir qué hacer con un iterador, primero debe saber si este iterador es inválido o no, si llama a la función de borrado del contenedor que está utilizando.

He creado una función que comprueba, prueba, sabe y devuelve verdadero si un iterador dado es inválido o no. Puede usar la función memcpy para obtener el estado de cualquier objeto, elemento, estructura, clase y etc., y por supuesto siempre usamos la función memset al principio para borrar o vaciar un nuevo búfer, estructura, clase o cualquier objeto o elemento. :

bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary. { byte buffer[sizeof(i)]; memset(buffer, 0, sizeof(i)); memcpy(buffer, &i, sizeof(i)); return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators". }

Ya he probado esta función antes de publicarla allí y encontré que esta función está funcionando para mí.

¡Espero haber respondido completamente a tu pregunta y también haberte ayudado mucho!


En algunos de los contenedores STL, el iterador actual deja de ser válido cuando borra el valor actual del iterador. Esto sucede porque la operación de borrado cambia la estructura de memoria interna del contenedor y el operador de incremento en los puntos iteradores existentes a una ubicación indefinida.

Cuando hace lo siguiente, el iterador se incentra antes de pasar a la función de borrado.

if (something) l.erase(itd++);


Por lo general, lo prueba comprobando si es diferente del final (), como

if (it != container.end()) { // then dereference }

Además, usar el manejo de excepciones para reemplazar la lógica es malo en términos de diseño y rendimiento. Su pregunta es muy buena y definitivamente vale la pena reemplazarla en su código. El manejo de excepciones como los nombres dice solo se utilizará para problemas inesperados raros.


Probar y atrapar no es seguro, no lo harás, o al menos rara vez lanzarás si tu iterador está "fuera de límites".

lo que dicen los alemjerus, un iterador siempre puede ser desreferido. No importa qué fea esté debajo. Es muy posible iterar en otras áreas de la memoria y escribir en otras áreas que puedan mantener otros objetos. He estado mirando el código, observando cambios en las variables sin ninguna razón en particular. Ese es un error que es muy difícil de detectar.

También es aconsejable recordar que insertar y eliminar elementos podría invalidar todas las referencias, punteros e iteradores.

Mi mejor consejo sería mantener sus iteradores bajo control y tener siempre a mano un iterador de "fin" para poder probar si está al "final de la línea", por así decirlo.


Respuesta no portátil: Sí - en Visual Studio

Los iteradores STL de Visual Studio tienen un modo de "depuración" que hace exactamente esto. No querría habilitar esto en las compilaciones de envío (hay gastos generales) pero útil en las compilaciones verificadas.

Lea sobre esto en VC10 here (este sistema puede y de hecho cambia cada versión, así que encuentre los documentos específicos de su versión).

Editar También, debo agregar: los iteradores de depuración en Visual Studio están diseñados para explotar inmediatamente cuando los usas (en lugar de un comportamiento indefinido); No permitir "consultar" de su estado.


Supongo que quiere decir que "es un iterador válido", que no se ha invalidado debido a cambios en el contenedor (p. Ej., Insertar / borrar desde / a un vector). En ese caso, no, no puede determinar si un iterador es (con seguridad) no aplicable.


utilizar borrar con incremento:

if (something) l.erase(itd++);

para que pueda probar la validez del iterador.


if (iterator != container.end()) { iterator is dereferencable ! }

Si su iterador no es igual a container.end() y no se puede eliminar la referencia, está haciendo algo mal.