2009-02-03 11 views

Respuesta

21

Olvidar que los iteradores a menudo se invalidan si cambia el contenedor insertando o borrando miembros del contenedor.

Para muchas grandes consejos sobre el uso de STL I altamente recomiendan libro de Scott Meyers "STL eficaz" (sanitised Amazon link)

+2

En general, estoy de acuerdo, pero es posible interpretar su afirmación como "los iteradores son * siempre * invalidados por inserciones o eliminaciones". Los iteradores de vectores nunca se invalidan borrando un elemento * later *, y ni las inserciones ni las eliminaciones de otros elementos anulan la lista, establecen y asignan iteradores. –

+0

@j_random_hacker: es cierto, voy a editar mi respuesta –

+0

si es posible agregue un código – yesraaj

8

Post-incrementarse cuando pre-incrementación va a hacer.

+0

No puedo decir que esto sea un gran problema ya que no compromete la corrección del código, y seguramente un compilador de optimización decente generará código idéntico cada vez el valor original de pre-incremento de i ++ no se usa? (Tenga en cuenta que siempre hago pre-inc por si acaso ...) FTR: No lo hice -1 (tampoco lo hizo +1 ...). –

+0

@j_random_hacker, estoy totalmente de acuerdo en que no es gran cosa, pero es una expresión de iterador de STL común. La pregunta no especificaba qué tipos de temas específicos le preocupaban. –

+4

El compilador no puede necesariamente generar código idéntico, dependiendo de qué es realmente el iterador. i ++ genera un valor temporal, que no siempre puede optimizarse. ++ solo hago el incremento. –

9

La verificación del rango final debe estar utilizando! = Y no <, ya que no se garantiza el orden de los punteros.

Ejemplo:

for(it = list.begin(); it != list.end(); ++it) 
{ 
    // do stuff 
} 
+0

¡Ay! La implementación de la Biblioteca estándar de C++ debe utilizar (typedefs de) tipos de punteros sin procesar como iteradores; por esta razón, es mucho más seguro implementar todos los iteradores como tipos distintos que envuelven punteros, con sobrecargas de operador adecuadas (por ejemplo, para el operador ==() pero no el operador <()). –

+0

Sí, hace un tiempo que hice C++ para ser sincero. Nunca tuve problemas con él, ya que usaba principalmente vectores. Esta es una de las cosas que recuerdo de Exceptional C++ de Herb Sutter. (O algún otro libro similar) –

+0

Tiene un error tipográfico: debe comparar "it! = List.end()", no "it! = List.end". De lo contrario, estás comparando con la dirección de una función. – Tom

6

Su uso sin necesidad de leer el libro "STL eficaz" por Scott Meyers. :) De Verdad. Esto hace que la mayoría de los bichos estúpidos desaparezcan.

+0

Además de tener muchos buenos consejos y ejemplos sobre la mejor manera de utilizar el STL. –

7

Algunos otros:

  • Conversión de un iterador inverso en un iterador de base sin recordar que el iterador ahora será uno de los elementos más allá de la que estaba señalando.

  • Intentando usar algoritmos que requieren un iterador de acceso aleatorio con iteradores de cosas como conjuntos y mapas.

  • recomienda modificar la clave de una entrada de mapa con un iterador no constante (esto sucede a construir sobre VS.Net, pero no lo hará con GCC)

6

continuación adecuada después de erase().

Suponiendo:

Container::iterator i = cont.begin(), iEnd = cont.end(); 

Por ejemplo en std::map, esto no es una buena idea:

for (; i != iEnd; ++i) { 
    if (i->second.eraseCondition()) { 
     cont.erase(i); 
    } 
} 

esto funcionaría:

for (; i != iEnd;) { 
    Container::iterator temp = i; 
    ++temp; 
    if (i->second.eraseCondition()) { 
     cont.erase(i); 
    } 
    i = temp; 
} 

Y esto también:

for (; i != iEnd;) { 
    if (i->second.eraseCondition()) { 
     cont.erase(i++); 
    } 
    else { 
     ++i; 
    } 
} 

Ha sido demasiadas veces realmente que he tenido que aplicar estas correcciones en algún código de producción :(

+0

Para esto, debe usar std :: remove_if para evitar este tipo de errores. –

+0

@Billy: Pero nadie lo hace porque sin lambdas tu código parece prolijo y estúpido. –

+0

@ Zan: 1. ¿Alguna estadística que respalde eso? 2. No sé ustedes, pero tomaré un código de apariencia un poco "estúpido" para convertir un algoritmo O (n^2) en un algoritmo O (n). –

3

El uso de un auto_ptr dentro de un contenedor, por ejemplo,

list<auto_ptr<int> > foo; 

Afortunadamente, muchas implementaciones auto_ptr estos días están escritos para hacer que esto sea imposible.

+0

Nota: Use boost :: shared_ptr <> en lugar de std :: auto_ptr <> si desea almacenarlos en un contenedor. – Frank

2

Esto no es solo un problema con los contenedores STL: el contenedor modyfing al iterar sobre él casi siempre genera problemas.

Esta es una fuente muy común de errores en los juegos: la mayoría de los bucles de juego consisten en iterar sobre cada objeto del juego haciendo algo. Si esto algo agrega o borra elementos del contenedor de objetos del juego, seguramente habrá errores.

Solución: tenga dos contenedores, objectsToDelete y objectsToAdd, en el código del juego agregue objetos a esos contenedores y actualice el contenedor de objetos del juego solo después de iterar sobre él.

objetsToAdd puede establecerse para garantizar que no eliminaremos nada más de una vez.

objectsToDelete puede ser cola si se puede construir objetos sin agregarlo a juego contenedor de objetos, o puede ser alguna otra clase (ObjectCreateCommand?) Si el código asume esa instancia de objeto siempre se añade al juego de objetos contenedores directamente después de la creación (por ejemplo en constructor).

+0

Tengo que estar más de acuerdo con este: he visto el bucle de objetos infinitamente creciente muchas veces. También se ve la omisión del elemento después del elemento eliminado e incluso el reprocesamiento del elemento antes del elemento eliminado. –

1
list<int> l1, l2; 
// ... 

for_each(l1.begin(), l2.end(), do_it()); 
Cuestiones relacionadas