Contenedores de STL
Contenedores de STL
Contenedores de STL
Qu es un contenedor?
Un contenedor es un elemento de cdigo que nos permite
almacenar y recuperar un conjunto de datos de manera
cmoda y eficiente.
Existen diferentes contenedores en funcin de cmo queremos
que se gestione su almacenamiento en memoria.
No existe un contenedor mejor que otro, solo uno ms
adecuado para cada caso, por eso es importante conocer las
particularidades de cada contenedor para saber cual emplear
en cada situacin.
STL no tiene restricciones sobre el tipo de datos que podemos
guardar en un contenedor, ya que se basa en templates.
Templates
Si pongo MyClass<float> m en
algun lugar el compilador compilar:
class MyClass
{
float data;
void setData(float v);
};
Iteradores
Qu es un iterador?
Un iterador es una estructura pensada para apuntar a un elemento de
un contenedor.
Tiene ciertas ventajas, la principal es que homogenizan el acceso a
un contenedor sin importar de qu clase es el contenedor.
Tambin permiten saltar de un elemento al siguiente o al anterior, o
saber si hemos llegado al final de un contenedor.
Los iteradores son muy importantes ya que estan muy optimizados y
para ciertos contenedores son la nica manera que tenemos de poder
recorrer todos sus elementos.
Adems muchas de las funciones de STL requieren como parmetros
de entrada iteradores, por ejemplo si queremos ordenar un rango de
elementos de un contenedor necesitamos especificar el primero y el
ltimo mediante iteradores.
Iteradores
Los iteradores se declaran teniendo en cuenta el tipo de contenedor:
std::list<int>::iterator it;
ya que tiene que coincidir con el tipo de datos.
Si queremos acceder al elemento que apunta un iterador usamos el
operador asterisco:
(*it)->doSomething();
Algunos iteradores pueden tener dos valores (key y datos), en tal
caso usamos el operador flash: it->first y it->second
Iteradores, trucos
Si necesitamos iterar al revs (de atrs a adelante) usamos
r_begin() y r_end()
Atencin: No se deben modificar los contenedores mientras se
itera por ellos, esto podra invalidar el iterador, y producir un
error.
Si nos da pereza escribir el tipo del iterador:
std::map<std::string, MyClass*>::iterator it = container.begin();
Interfaz comn
Todos los contenedores de STL usan un interfaz comn, esto
significa que la manera de acceder a ellos es similar.
Por ejemplo, para saber el nmero de elementos hacemos
mycontainer.size() sin importar si es una lista o un
vector.
Si queremos vaciarlo podemos usar mycontainer.clear()
Adems tienen los operadores de copia ya definidos por lo que
podemos copiar un contenedor a otro fcilmente:
//esto copia todos los valores de un contenedor al otro
my_container = my_old_container;
Encontrar elementos
A veces necesitamos saber si un elemento est dentro de un
contenedor o la posicin que ocupa, para eso usamos el
mtodo find.
Este mtodo nos retorna un iterador al elemento, si el
elemento no est retorna un iterador apuntando a end()
Hay que tener en cuenta que el coste computacional del find
depender del tipo de contenedor.
std::list<int>::iterator it = my_container.find( value );
if ( it != my_container.end() )
std::cout << "Found!" << std::endl;
Borrar elementos
Para borrar un elemento necesitamos tener un iterador al elemento
que queremos borrar, para ello podemos utilizar mtodos como find o
iterar hasta llegar al elemento que queremos.
my_container.remove( it );
Si borramos un elemento de un contenedor el iterador queda
invalidado, es importante tenerlo en cuenta o podemos colgar la
aplicacin.
No podemos hacer remove(it) y luego it++
Hay que recordar que el borrado de elementos en ciertos tipos de
contenedores es poco eficiente, por ejemplo en un vector ya que
requiere reposicionar todos los elementos que le siguen.
Tipos de contenedores
Vectores (std::vector)
Un vector es similar a un array, solo que gestiona automaticamente la memoria
(reserva el espacio necesario, hace copias, etc)
Nos permite almacenar secuencialmente en memoria una serie de datos.
Ventajas
permite hacer accesos aleatorios instantaneos.
permite iterar rapidamente por los elementos.
no requiere iteradores para acceder a los elementos.
Inconvenientes
Insertar elementos o borrar elementos es lento, ya que implica reorganizar
el resto de datos.
Si el tamao del vector se incrementa esto implica reservar toda la memoria
de nuevo y hacer una copia de la anterior a la nueva posicin, por lo que no
es ideal en casos donde no sabemos cuantos elementos tendremos.
Hacer busquedas dentro del contenedor tiene un coste computacional
elevado ya que implica recorrerselos todos hasta dar con l.
Vectores: sintaxis
#include <vector>
//creamos un vector de enteros
std::vector< int > my_vector;
//cambia el tamao del contenedor
my_vector.resize(10);
//iteramos por todos los elementos
for (int i = 0; i < my_vector.size(); i++)
my_vector[ i ] = rand() % 50; //asignamos un valor
aleatorio
//aadir al final un elemento nuevo
my_vector.push_back( 100 );
String
#include <string>
std::string my_text = "hola soy yo";
my_text.size(); //indica el numero de caracteres
my_text[0]; //retorna el primer caracter
my_text.substring(5,3); //retorna el string "soy"
std::string my_new_text = my_text; //copia
Listas (std::list)
Es una lista donde cada elemento est enlazado con el siguiente
formando una cadena.
Sirve para almacenar datos secuencialmente, similar a un vector pero
con ligeras diferencias.
Ventajas
Insertar y eliminar valores tiene un coste fijo.
Agregar ms valores no supone ningun coste extra
No necesitamos saber cuantos valores habr.
Inconvenientes
No permite accesos aleatorios, tenemos que recorrernos la lista
usando iteradores si queremos ir a una posicion.
Fragmenta la memoria ya que cada valor se almacena en una
direccin diferente.
Encontrar un elemento tiene un coste computacional elevado.
Listas: Sintaxis
#include <list>
std::list<int> my_list;
//insertamos dos elementos
my_list.push_back(5);
my_list.push_back(15);
//iteramos
for (std::list<int>::iterator it = my_list.begin();
it != my_list.end(); it++)
{
//accedemos al elemento usando el iterador
std::cout << "Elemento: " << *it << std::endl;
}
Conjunto (std::set)
Sirve cuando queremos almacenar un conjunto de datos pero
no nos importa el orden, se comporta como una hash table
donde la key es el propio valor.
Ventajas
Encontrar un elemento tiene un coste bajo.
Ordena los valores automaticamente.
Inconvenientes
Iterar secuencialmente los valores tiene un coste elevado.
Es obligatorio que el tipo de datos almacenado permita
hacer comparaciones entre los datos (necesario para el
sistema de almacenamiento basado en ordenacin).
Set (sintaxis)
#include <set>
std::set<std::string> my_tags;
my_tags.insert("cold");
my_tags.insert("white");
my_tags.insert("water");
my_tags.insert("cold"); //repetimos uno
std::set<std::string>::iterator it; //iterador
for (it=my_tags.begin();it != my_tags.end(); it++)
std::cout << *it << ",";
Esto mostrara: cold,water,white,
Map (sintaxis)
#include <map>
std::map<std::string, int> my_scores;
//puedes insertar usando el operador []
my_scores["javi"] = 100;
my_scores["juan"] = 50;
//busca un elemento con la key "javi"
std::map<std::string, int>::iterator it;
it = my_scores.find("javi");
//si el elemento est imprime su valor
if (it != my_scores.end() )
std::cout << "The score of " << it->first << " is " <<
it->second << std::endl;
//imprimir "The score of javi is 100"
Map
Varias consideraciones sobre el map:
Podemos insertar usando el operador corchete (mymap
["test"] = 10), pero no debemos usar ese operador para ver
si existe un valor ya que dicho operador estar creando el
elemento, hay que usar el metodo find.
Si queremos usar strings como key debemos especificar
como tipo de datos de la key un std::string, no un char* o
const char*, ya que si usamos const char* usar la direccin
del puntero como key, no donde apunta.
Si queremos usar como key una clase propia podemos
hacerlo, pero esa clase debe tener definidos los operadores
de igualdad y comparacin.
Algoritmos y transformaciones
Existen ademas funciones especiales pensadas para aplicar
acciones a los elementos de un contenedor.
Por ejemplo ordenar los valores (util para listas o vectores),
aplicar una funcin a cada valor, encontrar el mximo o el
mnimo de un contenedor, etc.
Para ello definimos el callback necesario y se lo pasamos a la
funcin que aplica la accin.
Mirad la referencia del archivo <algorithm>
http://www.cplusplus.com/reference/algorithm/
Conclusiones
STL nos ahorra mucho trabajo de desarrollo.
Es eficiente, se basan en templates para acelerar la
ejecucin.
Es multiplataforma
Permite detectar errores que de otro modo seran poco
visibles.
Casi todos los usos que podamos pensar estan ya
implementados.