2008-10-09 8 views
34

Estoy escribiendo algunos códigos multiplataforma entre Windows y Mac.¿Cómo se itera hacia atrás a través de una lista STL?

If list :: end() "devuelve un iterador que dirige la ubicación que sucede al último elemento en una lista" y se puede verificar al atravesar una lista, ¿cuál es la mejor forma de retroceder?

Este código workson el Mac, pero no en Windows (no se puede decrementar allá primer elemento):

list<DVFGfxObj*>::iterator iter = m_Objs.end(); 
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ? 
{ 
} 

esto funciona en Windows:

list<DVFGfxObj*>::iterator iter = m_Objs.end(); 
    do{ 
     iter--; 
    } while (*iter != *m_Objs.begin()); 

¿Existe otra forma de recorrer hacia atrás que podría implementarse en un bucle for?

+1

Sería solo un accidente de implementación que tu primer ejemplo (iterador circular, comparando con end()) funcionaría. – Justsalt

Respuesta

60

Uso reverse_iterator en lugar de iterador. Uso rbegin() & rend() en lugar de begin() & final().

Otra posibilidad, si te gusta usar la macro BOOST_FOREACH es utilizar la macro BOOST_REVERSE_FOREACH introducida en Boost 1.36.0.

+0

La documentación para iterator y reverse_iterator es casi la misma. un iterador es bidireccional, ¿cuál es la diferencia? – AlanKley

+2

La diferencia es que todavía se usa "++ Iter" para incrementar el iterador, frente a "--Iter". ¿O estoy equivocado? – steffenj

+0

No, tienes razón, lo cual es un poco extraño aumentar para ir hacia atrás, pero también tiene sentido. Aunque el reverse_iterator parece innecesario dado que el iterador es bidireccional. Los documentos para reverse_iterator dicen que actúa en una lista invertida; seguramente no revierte la lista internamente primero. – AlanKley

13

Probablemente quieren que los iteradores inversa. De memoria:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin(); 
for(; iter != m_Objs.rend(); ++iter) 
{ 
} 
+1

Gracias que suena bien. Pero también parece un desperdicio crear un reverse_iterator especial cuando se supone que el iterador es bidireccional. – AlanKley

+0

debe ser "...> :: reverse_iterator iter = ..." – steffenj

+0

@AlanKley Creo que el ciclo de bucle que has puesto tu pregunta esta bien La razón por la que creo que funciona es porque la función miembro .end() devuelve un valor centinela que se asigna como el valor del siguiente puntero desde el último elemento y también el puntero anterior en el primer elemento. –

4

Como ya se ha mencionado por Ferruccio, el uso reverse_iterator:

for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i) 
5

esto debería funcionar:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin(); 
for (; iter!= m_Objs.rend(); iter++) 
{ 
} 
16

La forma mejor/más fácil de revertir iterar una lista es (como ya indicado) para usar los iteradores inversos rbegin/rend.

Sin embargo, quería mencionar que los iteradores inversos se implementan almacenando la posición del iterador "actual" de a-uno (al menos en la implementación de GNU de la biblioteca estándar).

Esto se hace para simplificar la implementación, para que el rango en sentido inverso para tener la misma semántica que un rango de avance [comenzar, terminar) y [rbegin, rend)

Lo que esto significa es que dereferenciar una iterador implica la creación de un nuevo temporal, y luego disminuyendo ella, cada vez:

reference 
    operator*() const 
    { 
_Iterator __tmp = current; 
return *--__tmp; 
    } 

Por lo tanto, la eliminación de referencias un reverse_iterator es más lento que un iterador normal.

Sin embargo, su lugar, puede utilizar los iteradores bidireccionales regulares para simular revertir iteración a sí mismo, evitando esta sobrecarga:

for (iterator current = end() ; current != begin() ; /* Do nothing */) 
{ 
    --current; // Unfortunately, you now need this here 
    /* Do work */ 
    cout << *current << endl; 
} 

pruebas mostraron que esta solución sea ~ 5 veces más rápido para cada desreferenciar utilizado en el cuerpo del bucle

Nota: Las pruebas no se hizo con el código anterior, como el std :: cout habría sido el cuello de botella.

También Nota: la diferencia de "tiempo de reloj de pared" fue ~ 5 segundos con un tamaño de lista estándar de 10 millones de elementos. Entonces, de manera realista, a menos que el tamaño de tus datos sea tan grande, solo mantente en rbegin() rend()!

+0

Mirando esto de nuevo, Probablemente quiera simplemente inicializar con current = --end(); y deje el paso de incremento dentro del ciclo for. Esto también protegería contra la matriz vacía que mi versión anterior no lo hace. Dejaré el original publicado por ahora ya que no he probado. – mmocny

+0

No creo que esto funcione a menos que también cambie su condición de bucle. De lo contrario, perderá el primer elemento (porque el ciclo no se ejecutará si 'current == begin()') – Griddo

+0

La primera línea del ciclo for hace una disminución, así que sí, creo. ¡Simplemente escribe una prueba rápida para probarlo! De todos modos, ya no creo que esta sea la mejor manera de usar este modismo, y algunas pruebas recientes que ejecuté ya no muestran una mejora de velocidad marcada para invertir iteradores en optimización completa. Sin embargo, vale la pena seguir observando lo que está sucediendo bajo el capó y probando en consecuencia! – mmocny

Cuestiones relacionadas