La STL

La STL (Standard Template Library) es una parte de la biblioteca estándar de C++, que se ocupa de proveer al programador de varios tipos de estructuras de datos (listas, vectores, pilas ... etc.), ya probados y preparados para funcionar con el máximo de eficiencia.

Por poner un ejemplo, en la STL hay una estructura de datos (contenedor, en terminología C++), llamada stack, que implementa una pila:


#include <stack>
#include <iostream>

using namespace std;

int main()
{
stack<int> s;

s.push( 1 );
s.push( 2 );
s.push( 3 );

while( !s.empty() ) {
cout << s.top() << endl;
s.pop();
}

}

stack proporciona una interfaz de sobra conocida: push y pop. Con la única salvedad de que para obtener el tope de la pila, se emplea top(), y sólo top(), mientras que para eliminarlo, sólo se puede utilizar pop() (que devuelve void).

Vectores en la STL

Uno de los contenedores más útiles de la STL es Vector. Implementa un vector de elementos consecutivos en memoria. Permite la adición de elementos (con coste O(1)), la inserción en cualquier punto del vector (con coste O(n), claro), eliminación en cualquier punto del vector (con coste O(n)), y el acceso a los elementos.

Un ejemplo de uso de la estructura de datos Vector es el siguiente:

#include <vector>
#include <iostream>

using namespace std;

void muestra(const vector<int> &v)
{
for(unsigned int i = 0; i < v.size(); ++i ) {
cout << v[ i ] << endl;
}
}

int main()
{
vector<int> s;

s.push_back( 1 );
s.push_back( 2 );
s.push_back( 3 );

muestra( v );
}


size() devuelve el número de elementos en un vector.

Pero para recorrer un vector, es posible emplear también iteradores (iterators). Los iteradores son como punteros, pero encapsulan gran parte de su comportamiento, de manera que sean más sencillos de usar. Los iteradores siempre son de un acceso mucho más rápido que el operador [].


void muestraRapida(const vector<int> &v)
{
vector<int>::const_iterator it = v.begin();
while( it != v.end() ) {
cout << *it << endl;
++it;
}
}


Los iteradores constantes (const_iterators), sólo tienen permitido acceder a un elemento (mediante el operador *) para leerlo, pero no para modificarlo. Si queremos sumar uno a todos los elementos del vector:


void sumaUno(vector<int> &v)
{
vector<int>::iterator it = v.begin();
while( it != v.end() ) {
++(*it);
++it;
}
}



[Sólo frikies]
También se puede hacer en plan "resumido", como:

void sumaUno(const vector<int> &v)
{
vector<int>::iterator it = v.begin();
while( it != v.end() ) ++(*it++);
}
[/Sólo frikies]

Listas de la STL

La listas de la STL se crean mediante el encabezado (contenedor) list. Para poder recorrerla, sólo se pueden usar iteradores. Hay un método size(), pero es O(n), ya que necesita recorrer toda a lista para ir contando los elementos. A cambio, todas las operaciones (inserción, borrado ... etc.) son O( 1 ), excepto el acceso, que es secuencial, y por tanto O(i), siendo i el elemento al que deseamos acceder.


#include <iostream>
#include <list>

using namespace std;

void muestra(const list<int> &l)
{
list::const_iterator it = l.begin();
while( it != l.end() ) {
cout << *it << endl;
++it;
}
}

int main()
{
list<int> l;

l.insert( l.end(), 1 ); // { 1 }
l.insert( l.end(), 2 ); // { 1, 2 }
l.insert( l.begin(), 3 ); // { 3, 1, 2 }

muestra( l );
}


A veces es interesante acceder al elemento i de la lista. Para ello, contamos con advance(), en la cabecera utility, que permite hacer avanzar un iterador fácilmente. Nótese que las listas están enlazadas hacia delante y hacia atrás, así que es posible hacer tanto --it como ++it.



#include <utility>

usigned namespace std;

int main()
{
typedef list<int> ListaNumeros;
typedef ListaNumeros::iterator ItListaNumeros;
ListaNumeros l;
ItListaNumeros it;

l.insert( l.end(), 1 );
l.insert( l.end(), 2 );
l.insert( l.end(), 3 );

it = l.begin();
advance( it, 2 );

l.insert( it, 33 );

advance( it, -1 ); // ó "--it;"

l.insert( l.end(), 22 );

muestra( l );
}

Como se puede ver, muchas veces se puede emplear typedef para hacer más sencillo el manejo de plantillas.