sentencia - Iterar a través de un Vector C++ usando un bucle ''for''
javascript for each (7)
¿Hay alguna razón por la que no veo esto en C ++? ¿Es mala práctica?
No. No es una mala práctica, pero le da cierta flexibilidad a tu código.
Por lo general, pre-C ++ 11 el código para iterar sobre elementos contenedores utiliza iteradores, algo así como:
std::vector<int>::iterator it = vector.begin();
Esto se debe a que 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, entonces este código no necesita ser cambiado.
Nota: Escribir código que funcione con todos los contenedores de bibliotecas estándar posibles no es tan fácil como podría parecer.
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 una ArrayList:
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
¿Hay alguna razón por la que no veo esto en C ++? ¿Es mala práctica?
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 operator []
en absoluto.
Hay un par de razones poderosas para usar iteradores, algunos de los cuales se mencionan aquí:
Cambiar de contenedor más tarde no invalida su 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 captura de iteración no válida
Si modifica su contenedor en el medio de su ciclo, la próxima vez que use su iterador arrojará una excepción de iterador no válido.
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, solo escriba CActividad en lugar de T.
Este tipo de método funcionará en cada STL (no solo vectores, que es un poco mejor).
Si aún desea usar índices, el camino es:
for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
v[i].doSomething();
}
La forma más limpia de iterar 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 lo anterior)
for (auto & element : vector) {
element.doSomething ();
}
Antes de C ++ 0x, debe reemplazar el auto por el tipo de iterador y usar las funciones de miembro en lugar de las funciones globales de inicio y fin.
Esto probablemente es lo que has visto. En comparación con el enfoque que mencionas, la ventaja es que no dependes en gran medida del tipo de vector
. Si cambia el 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 de begin
y end
, incluso para los tipos que no son clases, como las matrices estáticas. Vea aquí: ¿Cómo funciona el rango para trabajar con arreglos simples?
La razón por la que no ve esta práctica es bastante subjetiva y no puede tener una respuesta definitiva, porque he visto muchos de los códigos que usan su forma mencionada en lugar de código de estilo de iterator
.
A continuación puede haber razones por las que las personas no consideran la forma de bucle vector.size()
:
- Ser paranoico sobre el
size()
llamadasize()
cada vez en la condición de bucle. Sin embargo, o bien no es un problema o puede ser trivialmente resuelto -
std::for_each()
sobre el buclefor
- Más tarde, cambiar el contenedor de
std::vector
a otro (por ejemplo,map
,list
) también exigirá el cambio del mecanismo de bucle, porque no todos lossize()
soporte de contenedorsize()
estilo de bucle
C ++ 11 proporciona una buena facilidad para moverse a través de los contenedores. Eso se llama "rango basado en bucle" (o "mejorado para bucle" en Java).
Con poco código puedes atravesar el std::vector
completo (¡obligatorio!):
vector<int> vi;
...
for(int i : vi)
cout << "i = " << i << endl;
Me sorprendió que nadie mencionara que iterar a través de una matriz con un índice entero hace que sea más fácil escribir código defectuoso mediante la suscripción de una matriz con el índice incorrecto. Por ejemplo, si ha anidado bucles utilizando i
y j
como índices, puede subíndices incorrectos de una matriz con j
lugar de i
y, por lo tanto, introducir un error en el programa.
Por el contrario, las otras formas enumeradas aquí, es decir, el rango basado for
bucle e iteradores, son mucho menos propensas a errores. La semántica de la lengua y el mecanismo de verificación de tipos del compilador evitarán que accedas accidentalmente a una matriz con el índice incorrecto.