En el anterior artículo vimos una introducción a los contenedores. En él vimos los tipos de contenedores que hay, los archivos de cabecera y las funciones comunes de los contenedores. Ahora vamos a dedicar este segundo artículo a poner en práctica el uso de contenedores.
Como dijimos en el artículo anterior la STL está basada en plantillas de C++ lo que las hace versátil a cualquier tipo de dato que queramos almacenar. Vamos a poner en práctica el uso de los diferentes tipos de contenedores.
Vectores
La clase vector es la más común de los contenedores de la STL. Nos permite la inserción y extracción fácil en la parte final del contenedor y acceso rápido a cualquier elemento mediante índice. Veamos un ejemplo de uso.
#include <iostream> | |
#include <vector> // Cabecera necesaria para usar vector | |
int main() | |
{ | |
// Declaramos un vector de tipo int llamado v | |
std::vector<int> v; | |
// Añadimos valores al final con push_back | |
v.push_back(5); | |
v.push_back(8); | |
v.push_back(2); | |
v.push_back(3); | |
// Podemos usar el operador [] para acceder a las diferentes posiciones del vector | |
std::cout << "Pos 0: " << v[0] << std::endl; | |
std::cout << "Pos 3: " << v[3] << std::endl; | |
// La función size nos dice el tamaño del vector | |
std::cout << "tamanio de v: " << v.size() << std::endl; | |
// Podemos usar size para recorrer el vector. | |
for (int i = 0; i < v.size(); i++) | |
{ | |
std::cout << "Pos " << i << ": " << v[i] << std::endl; | |
} | |
// Usamos la función pop_back para quitar el último elemento | |
v.pop_back(); | |
// comprobamos el nuevo tamaño | |
std::cout << "tamanio de v: " << v.size() << std::endl; | |
} |
El programa nos daría el siguiente resultado.
Pos 0: 5 Pos 3: 3 tamanio de v: 4 Pos 0: 5 Pos 1: 8 Pos 2: 2 Pos 3: 3 tamanio de v: 3
Creo que los comentarios en el código son autoexplicativos de como usar las diferentes funciones típicas de la clase vector. Cabe destacar que la función size devuelve el tamaño del vector y que al igual que los arrays el índice comienza en 0.
La manera de acceder y recorrer el vector con una variable entera y haciendo uso de la función size no es la mejor manera de hacerlo, se recomienda el uso de iteradores, pero ya lo veremos en próximos artículos.
Vectores de vectores
Una práctica común es el uso de vectores de vectores, es decir, los llamados arrays bidimensionales con doble índice. Con los vectores de la STL lo haríamos de la siguiente manera.
#include <iostream> | |
#include <vector> // Cabecera necesaria para usar vector | |
// Creamos dos typedef para declarar vectores | |
typedef std::vector<int> VI; | |
typedef std::vector<VI> VVI; | |
int main() | |
{ | |
// Declaramos un vector de vector int llamado v | |
VVI v(6, VI(4)); // (6x4) | |
// A continuación lo podemos rrecorrer como un array bidimensional | |
for (int i = 0; i < v.size(); i++) | |
{ | |
for (int j = 0; j < v[i].size(); j++) | |
{ | |
v[i][j] = i+j; | |
} | |
} | |
for (int i = 0; i < v.size(); i++) | |
{ | |
for (int j = 0; j < v[i].size(); j++) | |
{ | |
std::cout << v[i][j] << " "; | |
} | |
std::cout << std::endl; | |
} | |
} |
Nos crearía la siguiente tabla:
0 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8
Puede parecer algo engorroso, pero no se suele usar mucho los vectores de esta manera para ello se usan otros tipos de datos.
deque
Las deque son casi iguales que los vectores, pero además de añadir y eliminar datos por el final también puedes hacerlo por la parte frontal y acceso rápido a cualquier elemento del mismo. Son algo más lentos que los vectores, pero más flexibles. Veamos un ejemplo.
#include <iostream> | |
#include <deque> // Cabecera necesaria para usar deque | |
int main() | |
{ | |
// Creamos un deque | |
std::deque<int> d; | |
// insertamos dos elementos al final | |
d.push_back(4); | |
d.push_back(5); | |
// insertamos dos elementos en la parte frontal | |
d.push_front(9); | |
d.push_front(2); | |
// imprimimos en pantalla | |
for (int i = 0; i < d.size(); i++) | |
{ | |
std::cout << "pos " << i << ": " << d[i] << std::endl; | |
} | |
} |
Esto tendría el siguiente resultado en pantalla:
pos 0: 2 pos 1: 9 pos 2: 4 pos 3: 5
list
De las list de momento solo diremos que perminte la inserción y eliminación fácil en cualquier parte ya que se trata de una estructura de datos doblemente enlazada. Como para usarla necesitamos hacer uso de los iteradores que aún no hemos repasado dejaremos el ejemplo de listas pendientes hasta entonces.
map y multimap
Map es un tipo de dato asociativo en el que se asigna una pareja de clave valor para acceder a los datos. Multimap es exactamente lo mismo que map, pero acepta claves duplicadas para los valores.
#include <iostream> | |
#include <string> | |
#include <map> // Cabecera necesaria para usar map | |
int main() | |
{ | |
// Creamos un map de string (clave) e int (valor) | |
std::map<std::string, int> m; | |
m["uno"] = 1; | |
m["dos"] = 2; | |
m["tres"] = 3; | |
std::cout << m["uno"] << std::endl; | |
std::cout << m["dos"] << std::endl; | |
std::cout << m["tres"] << std::endl; | |
} |
Con esto hemos repasado los principales contenedores de la STL, pero la verdadera potencia está en usarlos con los iteradores y los algoritmos que veremos en siguientes entregas que nos permitirán hacer casi de todo.