Categorías de conenedores de la STL
Elementos simples Elementos “aparejados”
std::vector std::map
std::deque std::multimap
std::list
std::set

Los contenedores de la Standard Template Library (STL) de C++ pueden dividirse en dos categorías. La mayoría de los contenedores acoge a elementos simples. Estos contenedores son vector, deque, list y set. Al contrario de los contenedores simples están los contenedores asociativos map y multimap que guaradan a una pareja (pair) de valores: una clave y un valor. Se llaman asociativos porque asocian claves con valores.

Internamente un mapa está implementado como un árbol binario que acoge un elemento simple de tipo pair<T_KEY, T_VALUE>. Desgraciadamente los árboles no son un elemento público de la STL aunque prácticamente todas las versiones los implementan.

Insertar

Un problema práctico con los contenedores asociativos es que insertar un nuevo elemento acaba en líneas muy largas. En lugar de escribir algo tan complicado como

std::map.insert(std::pair<T_KEY, T_VALUE>(my_key,  my_value));

preferiríamos un método

std::map.insert(my_key, my_value);

Desgraciadamente este método no forma parte del estándar. Una solución a este problema es crear una clase propia que hereda del mapa estándar. Esto trae la ventaja que podamos añadir tantas funciones que nos convienen y la desventaja que hay que escribir esta clase. Para hacerla completa debemos al menos ofrecer todos los constructores y métodos de operator=.

Nuestro nuevo método de insertar sería entonces algo así:

template <class T_KEY, class T_VALUE>
class Map : public std::map<T_KEY, T_VALUE>
{
    std::pair<iterator,bool> insert(const T_KEY& key, const T_VALUE& value)
    {
        return insert(std::pair<T_KEY, T_VALUE>(key, value);
    }
};

Operator[] para mapas constantes

Otra función práctica que muchas veces falta es el operator[](const T_KEY& key) para mapas constantes. La versión no constante crea un nuevo elemento para la clave si no existe. En un mapa declarado const no podemos hacer esto, pero podemos arriesgar una excepción. Está en manos del usuario del método de asegurar que sólo llama a operator[] const para claves existentes o capturar las excepciones. Aquí está el código:

template <class T_KEY, class T_VALUE>
class Map : public std::map<T_KEY, T_VALUE>
{
    template <class TKey, class TValue>
    const TValue& operator[](const TKey& key) const
    {
        // Esta línea causa una excepción cuando find devuelve end()
        return this->find(key)->second;
    }
};

Iteradores sólo sobre valores

Otra utilidad que falta son iteradores sólo sobre los valores del mapa, es decir un iterador cuyo operator* no devuelve un std::pair<T_KEY, T_VALUE> sino un T_VALUE. Uno puede conseguir esto escribiendo una clase value_iterator, que contiene el iterador de mapa habitual como elemento. Desafortunadamente esto conlleva mucho trabajo ya que será necesario implementar todas las funciones del iterador convencional – muy especialmente todos los operadores sobrecargados.

Conclusión

En fin, hemos propuesto algunas ideas de como se pueden ampliar contenedores estándar por métodos propios – en este caso el contenedor map. Conviene tener en mente que crear una clase propia que hereda de clases estándar es una manera más limpia de codificar que crear funciones globales, macros o simplemente copiar y pegar las expresiones complejas y feas que la interfaz del contenedor estándar nos obliga a tener.

Referencias

Anuncios