Sunteți pe pagina 1din 31

Contenedores de STL

Javi Agenjo 2010


Universitat Pompeu Fabra

Standard Template Library


La Standard Template Library es una librera estandar que
contiene todo tipo de contenedores pensados para trabajar en
C++. Al ser estndar viene en todos los compiladores de C.
Es multiplataforma y est optimizada para que funcionen de la
manera ms eficiente.
Adems usan un interfaz comn que facilita el trabajo con
diferentes tipos de contenedores.
Dentro de STL podemos encontrar vectores, listas, pilas,
conjuntos, tabla de hash y otros muchos tipos de
contenedores, adems de algoritmos para trabajar con ellos.

Por qu es importante usar STL


STL ha sido testeada durante dcadas por infinidad de
programadores por lo que es muy segura y fiable.
Ademas las diferentes implementaciones de STL estan
pensadas para sacar el mximo partido al hardware.
Evitan cometer fallos que puedan producir agujeros en la
memoria ya que toda la gestin de la memoria del contenedor
es automtica.
Al ser templates casi todo se resuelve en tiempo de
compilacin, por lo que es ms rpida que otros tipos de
contenedores.

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

Qu son los templates?


Los templates nos permiten crear clases en las que algun tipo de
datos no est especificado en la definicin de la clase, dejando la
libertad al programador de instanciar esta clase con un tipo especifico
ms adelante.
A ese tipo se le da un nombre genrico (por ejemplo. T).
Cuando el programador instancia una clase que viene de un template
le debe indicar el tipo de datos que quiere usar, y el compilador
reemplaza en el template el tipo genrico por el tipo que le indica y
acto seguido compila la clase.
Los templates usan la sintaxis <...> para indicar el parametro:
//aqu creamos un vector de enteros
std::vector<int> vector_de_enteros;

Qu son los templates?


Un ejemplo de template y el resultado generado al expandir el
template en tiempo de compilacin:
Defino un template:
template <T>
class MyClass
{
T data;
void setData(T v);
};

Si pongo MyClass<float> m en
algun lugar el compilador compilar:
class MyClass
{
float data;
void setData(float v);
};

Usar templates hace el cdigo ms genrico pero puede dar pie a


errores ya que no sabemos qu tipo se le pasar a una clase al definir
el template.

Por qu usa templates la libreria STL?


Porque de esta manera cualquier contenedor de STL puede
almacenar cualquier tipo de datos que queramos, incluso
clases creadas por nosotros.
Sin embargo hay que tener presente una cosa, algunos
contenedores requieren que el tipo de datos que le pasemos
tenga definidos algunos operadores, como el de igualdad
(A==B) o en el caso de los maps, el de "mayor qu" (A > B).
Si el tipo que le pasamos tiene algn error o no tiene los
operadores necesarios para ese contenedor, al expandirse el
template se generar un error de compilacin y el compilador
nos indicar el error en una lnea del template de STL lo cual
puede resultar confuso para el programador.

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: begin y end


Todo contenedor tiene dos mtodos que es importante tener en
cuenta a la hora de iterar:
begin() retorna un iterador al primer elemento del contenedor, si el
contenedor est vacio retorna lo mismo que end()
end() retorna un iterador a la posicin despus del ltimo elemento
del contenedor, no al ltimo elemento. Esto se hace para poder
detectar si hemos llegado al final del contenedor cuando estamos
iterando.
Es importante notar que si tratamos de leer el elemento al que apunta
end() la aplicacin fallar.
Podemos comparar iteradores para saber si apuntan al mismo lugar.

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();

C++ 99 (la ltima versin de C++) permite usar la palabra auto,


entonces el compilador deduce el tipo mediante la asignacin:
auto it = container.begin();
Pero no todos los compiladores lo soportan.

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

Tipos bsicos de STL


Contenedores
std::vector
std::string
std::list
std::set
std::map
Iteradores
iterator
reverse_iterator

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 );

Cadenas de texto (std::string)


Tcnicamente se comporta como un vector de chars pero con
funcionalidades aadidas para gestionar las cadenas.
Permite almacenar de manera segura una cadena de texto y
aplicar ciertas transformaciones. Ahorra tener que lidiar con
problemas relacionados con la memoria (strings demasiado
largos que se salen de rango, etc).
Ventajas
Gestiona la memoria de manera automtica, evitando la
mayor parte de los errores de memoria habituales.
Inconvenientes
En comparacin con los arrays de chars tiene un ligero
overhead.

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

podemos retornar facilmente strings sin preocuparnos de quien


es el propietario de la memoria ya que la clase hace copias
automaticamente de las cadenas.
Si necesitamos el puntero a const char* podemos obtenerlo:
const char* txt = my_text.c_str();

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,

Contenedores key=>value (std::map)


Similar al set, permite almacenar un conjunto de datos pero
usando una clave (key) para cada uno.
No es exactamente una tabla de hash ya que utiliza rboles
balanceados para almacenar los datos.
Ventajas
Permite hacer bsquedas muy rpidamente
Los costes de insercin/borrado no son elevados.
Inconvenientes
Iterar es muy lento.
Memoria fragmentada.
No pueden haber dos elementos con la misma clave.

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.

S-ar putea să vă placă și