Bienvenidos sean a este post, hoy revisitaremos un tema que ya hablamos.
Al final del post anterior dijimos que no es necesario crear nuestros propios vectores y listas enlazadas porque los propios del lenguaje funcionan mas que bien. Estos dos son parte de los contenedores STL. Sobre este tema ya hablamos en este post por si necesitan mas informacion. Y sobre vector hablamos en el siguientes post:
En este hablamos como trabaja, asi como tambien de los metodos propios para manejar la informacion, como tambien se puede obtener otros datos de los mismos. Vamos a repasar como trabaja vector y para ello primero analizaremos un ejemplo:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
for(int i = 1; i <= 3; i++)
{
v.push_back(i * 100);
}
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
for(const int& e : v)
std::cout << e << std::endl;
return 0;
}
En este ejemplo primero crreamos un objeto de tipo vector que almacenara valores de tipo int. Mediante un bucle agregaremos tres valores en el vector usando a push_back. Lo siguiente es mostrar la capacidad y el tamaño de nuestro vector. Y finalmente, mostrar los valores en el vector, compilemos y ejecutemos para ver como es su salida:
$ ./list
capacidad: 4
tamaño: 3
100
200
300
$
Como mencionamos cuando hablamos de estructura de datos secuenciales y citamos a vector, en este hablamos que cambia su tamaño si la capacidad y el tamaño son iguales. Por esta razon, capacidad siempre sera mayor a tamaño. El tamaño es la cantidad de elementos almacenados y capacidad nos informa cuantas mas nos quedan. Tomemos el codigo anterior y hagamos el siguiente cambio:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
v.reserve(10000);
for(int i = 1; i <= 3; i++)
{
v.push_back(i * 100);
}
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
for(const int& e : v)
std::cout << e << std::endl;
return 0;
}
En este caso, solo agregamos un llamado a la funcion reserve. Esta hara que reserve en memoria 10000 posiciones para nuestros elementos del vector. Este es un buen metodo para asegurarnos que esos espacios no seran utilizados por ninguna otra instruccion, especialmente si sabemos la cantidad de elementos que necesitamos almacenar. El resto sigue de la misma forma, compilemos para ver como es la salida:
$ ./list
capacidad: 10000
tamaño: 3
100
200
300
$
Observen como es el nuevo valor para almacenar elementos y el tamaño sigue siendo el mismo porque solo agregamos estos tres elementos. Tomemos el codigo anterior y realicemos el siguiente cambio:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
v.reserve(10000);
for(int i = 1; i <= 3; i++)
{
v.push_back(i * 100);
}
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
for(int i=0,j = 4; i < 7; i++, j++)
{
v.push_back(j * 100);
}
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
v.shrink_to_fit();
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
return 0;
}
La primera modificacion fue la eliminacion del bloque encargado de mostrar los valores en el vector pero seguimos mostrando la capacidad y el tamaño. Tenemos otro bucle para agregar mas valores, en este caso continuando los valores anteriores, y para ello usamos un valor para contar y otro que se incrementara para agregar al vector. Volvemos a mostrar la capacidad y el tamaño para ver los cambios creados con el bucle anterior. Aqui aplicamos un nuevo metodo, shrink_to_fit, y este se encarga de ajustar la capacidad al tamaño o los elementos agregados. Este metodo es util para cuando sabemos que ese sera el total de los elementos a almacenar y las posiciones reservadas no son necesarias. Para no desperdiciar memoria sin uso, con este metodo procedemos a liberarlas para ser utilizadas por otras instrucciones. Compilemos y veamos como es su salida:
$ ./list
capacidad: 10000
tamaño: 3
capacidad: 10000
tamaño: 10
capacidad: 10
tamaño: 10
$
Como pueden ver, realizo lo ultimo que comentamos. Donde mientras no llamamos a shrink_to_fit, la capacidad se mantiene en el espacio que reservamos. Por ultimo tenemos varias maneras de recuperar la informacion almacenada, para ello modificaremos el codigo anterior de la siguiente manera:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
for(int i = 1; i <= 10; i++)
{
v.push_back(i * 100);
}
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
std::cout << "v.at(2): " << v.at(2) << std::endl;
std::cout << "v[5]: " << v[5] << std::endl;
std::cout << "v.data()[8]: " << v.data()[8] << std::endl;
return 0;
}
Eliminamos mucho de lo anterior pero dejamos un bucle que agregara diez elementos al vector. Volvemos a mostrar su capacidad y tamaño y luego las tres formas de recuperar el valor de cada posicion. El primer caso, es el mas recomendado porque es el mas eficiente para ir a una determinada posicion en el vector y tambien devuelve una excepcion ante un error, pero de eso hablaremos en un momento. La segunda es la forma mas tradicional de recuperar un elemento de un array. El ultimo, es un metotdo similar al operador [] para recuperar la informacion. Compilemos y veamos como es la salida:
$ ./list
capacidad: 16
tamaño: 10
v.at(2): 300
v[5]: 600
v.data()[8]: 900
$
Aqui nos devolvio el valor en cada posicion pero esto sigue un poco mas. Recuerdan cuando comentamos que at nos devuelve una excepcion. Tomemos el codigo anterior y hagamos la siguiente modificacion:
#include <iostream>
#include <vector>
int main()
{
try
{
std::vector<int> v;
for(int i = 1; i <= 10; i++)
{
v.push_back(i * 100);
}
std::cout << "capacidad: " << v.capacity() << std::endl;
std::cout << "tamaño: " << v.size() << std::endl;
std::cout << "v.at(12): " << v.at(12) << std::endl;
std::cout << "v[5]: " << v[5] << std::endl;
std::cout << "v.data()[8]: " << v.data()[8] << std::endl;
return 0;
}
catch(std::out_of_range& e)
{
std::cout << "Te fuiste del rango" << std::endl;
}
}
La primera modificacion fue agregar un try/catch para capturar una excepcion. Hablemos primero del catch, donde actuara si la excepcion es out_of_range. Es decir, cuando intentemos tomar un valor en un rango inexistente, si se activa mostraremos un mensaje relacionado con esto. En el try solamente modificamos al llamado de at donde le pasamos una posicion inexistente, recuerden que llega hasta 9. Compilemos y veamos que sucede:
$ ./list
capacidad: 16
tamaño: 10
v.at(12): Te fuiste del rango
$
Esto es una clara ventaja con respecto a los otros porque podemos manejar mejor a nuestro codigo ante una eventual excepcion. Con esto hemos completado un poco mas a vector y otros metodos que disponemos para manipularlo, asi como algunas conductas que mencionamos al momento de hablar sobre estructuras de datos secuenciales.
En resumen, hoy hemos visto a vector, nuevamente, como se compone, para que sirve, algunas conductas propias de este, una serie de ejemplos para aplicar varios metodos, asi como tambien algunas particularidades. Espero les haya sido deutilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donatión
It’s for site maintenance, thanks!
$1.50
