2012-10-03 85 views
83

Soy nuevo en el lenguaje C++. He estado empezando a usar vectores, y he notado que en todo el código que veo iterar a través de un vector a través de índices, el primer parámetro del bucle for siempre está basado en el vector. En Java podría hacer algo como esto con un ArrayList:Iterar a través de un C++ Vector utilizando un bucle 'for'

for(int i=0; i < vector.size(); i++){ 
    vector[i].doSomething(); 
} 

¿Hay una razón por la que no veo esto en C++? ¿Es mala práctica?

+0

El bucle for no es una función, por lo que no tiene parámetros (o argumentos, que es lo que le pasa). ¿Quiere decir algo como 'std :: vector :: size_type i = 0;', o quizás 'std :: vector :: iterator it = vector.begin();'? – chris

+0

Exactamente, todos los ejemplos que veo están escritos así. – Flynn

+4

En Java, preferiría un bucle for-each o usar iteradores. Más o menos lo mismo que C++ aunque sintaxis ligeramente diferente. –

Respuesta

59

alguna razón no veo esto en C++:

Si aún desea utilizar índices, el camino es? ¿Es mala práctica?

No. No es una mala práctica, pero hace que su código cierta flexibilidad.

Por lo general, pre-C++ 11 del código para iterar sobre los elementos de contenedor utiliza iteradores, algo así como:

std::vector<int>::iterator it = vector.begin(); 

Esto es porque hace que el código sea más flexible.

Todos los contenedores de bibliotecas estándar admiten y proporcionan iteradores, y dado que si en un punto posterior de desarrollo necesita cambiar otro contenedor, no es necesario cambiar este código.

Nota: Escribir el código que funciona con todos los contenedores de bibliotecas estándar posibles no es tan fácil como podría parecer.

+16

¿Alguien podría explicarme por qué en este caso particular/fragmento de código aconseja a los iteradores sobre la indexación? ¿De qué es esta "flexibilidad" de la que estás hablando? Personalmente, no me gustan los iteradores, inflan el código, simplemente más caracteres para escribir para el mismo efecto. Especialmente si no puedes usar 'auto'. –

+5

@VioletGiraffe: Al usar iteradores es difícil equivocarse con ciertos casos como rangos vacíos y el código es más detallado. Por supuesto, es una cuestión o percepción y elección, por lo que puede debatirse interminablemente. –

50

La forma más limpia de iteración a través de un vector es a través de iteradores:

for (auto it = begin (vector); it != end (vector); ++it) { 
    it->doSomething(); 
} 

o (equivalente a la anterior)

for (auto & element : vector) { 
    element.doSomething(); 
} 

Antes de C++ 0x, usted tiene que reemplazar por auto el tipo de iterador y el uso de funciones miembro en lugar de las funciones globales comienzan y terminan.

Esto probablemente es lo que has visto. En comparación con el enfoque que menciona, la ventaja es que no depende en gran medida del tipo de vector. Si cambia vector a una clase diferente de "tipo de colección", su código probablemente aún funcione. Sin embargo, también puedes hacer algo similar en Java. No hay mucha diferencia conceptualmente; C++, sin embargo, usa plantillas para implementar esto (en comparación con los genéricos en Java); por lo tanto, el enfoque funcionará para todos los tipos para los que se definen las funciones begin y, incluso para tipos que no son de clase, como matrices estáticas. Ver aquí: How does the range-based for work for plain arrays?

+4

auto, comienzo/final libre también son C++ 11. Y también, debería usar ++ it, en lugar de ++ en muchos casos. – ForEveR

+0

Sí, tienes razón. Implementar 'begin' y' end', sin embargo, es de una sola línea. – JohnB

+0

@JohnB es más que un trazador de líneas, ya que también funciona para arreglos de tamaño fijo. 'auto' por otro lado sería bastante complicado. – juanchopanza

27

La forma correcta de hacerlo es:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) { 
    it->doSomething(); 
} 

donde t es el tipo de la clase dentro del vector. Por ejemplo, si la clase era CActividad, simplemente escriba CActividad en lugar de T.

Este tipo de método funcionará en cada STL (no solo en los vectores, que es un poco mejor). no

for(std::vector<T>::size_type i = 0; i != v.size(); i++) { 
    v[i].doSomething(); 
} 
+0

no es 'std :: vector :: size_type' always' size_t'? Ese es el tipo que siempre uso para eso. –

+1

@VioletGiraffe Estoy bastante seguro de que tienes razón (en realidad no lo he comprobado), pero es una mejor práctica usar std :: vector :: size_type. – DiGMi

+0

gracias por mostrar el camino del índice "prohibido". – Rosenthal

4

Con STL, los programadores usan iterators para atravesar contenedores, ya que el iterador es un concepto abstracto, implementado en todos los contenedores estándar. Por ejemplo, std::list no tiene ningún operator [] en absoluto.

67

La razón por la que no se ve esta práctica es bastante subjetiva y no puede tener una respuesta definitiva, porque he visto muchos de código que utiliza el código en lugar de iterator estilo mencionado camino.

Siguiendo pueden ser motivos de las personas no consideran vector.size() modo de bucle:

  1. ser paranoico acerca de llamar size() cada vez que en el bucle condición. Sin embargo, ya sea que es un no-tema o puede ser trivialmente fija
  2. Prefiriendo std::for_each() sobre el bucle for
  3. Más tarde cambiar el recipiente de std::vector a otro (por ejemplo map, list) también exigir el cambio de la bucle mecanismo, porque no todos los soporte size() estilo contenedor de bucle

C++ 11 proporciona una buena facilidad para moverse a través de los recipientes. Eso se llama "rango basado en bucle" (o "mejorado para bucle" en Java).

Con poco código se puede recorrer a través de la plena std::vector (obligatorio!):

vector<int> vi; 
... 
for(int i : vi) 
    cout << "i = " << i << endl; 
+4

Solo para señalar una pequeña desventaja de _range para loop_: no se puede usar con '#pragma omp parallel for'. – liborm

+1

Me gusta la versión compacta porque hay menos código para leer. Una vez que haces el ajuste mental es mucho más fácil de entender y los errores se destacan más. También lo hace mucho más obvio cuando hay una iteración no estándar porque hay una porción mucho mayor de código. –

5

Hay un par de razones contundentes para usar iteradores, algunos de los cuales se mencionan aquí:

contenedores de conmutación más tarde no invalida tu código

es decir, si va de std :: vector a std :: list, o std :: set, no puede usar índices numéricos para obtener su valor contenido. El uso de un iterador sigue siendo válido.

tiempo de ejecución de la captura de iteración no válida

Si modifica su contenedor en medio de su bucle, la próxima vez que utilice el iterador se lanzará una excepción iterador válido.

Cuestiones relacionadas