Contenedores C++

Revisión del 17:17 20 jun 2019 de Javiermartin jc (discusión | contribuciones) (Texto reemplazado: «<div align="justify">» por «»)
(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Contenedores C++
Información sobre la plantilla
Concepto:En sentido C++ estricto, un contenedor de la STL es una clase genérica que puede instanciarse para representar diversos tipos de objetos. Esta clase incluye ciertas operaciones sobre los objetos de su tipo; estas operaciones están representadas por funciones-miembro, incluyendo constructores y funciones-operador, que son a su vez funciones genéricas

Contenedores C++ . Al mismo tiempo, desde el punto de vista de las teorías de la información, podemos considerarlos estructuras de datos más o menos adecuadas (según el caso) para aplicarles ciertos algoritmos.

De su propio nombre ("Containers") podemos deducir que su misión principal, y razón de su existencia, es su función como estructuras de datos, y en este sentido podemos adelantar que entre sus funcionalidades más destacadas se encuentran la gestión del espacio de almacenamiento necesario. Una de las primerísimas ventajas de la utilización de contenedores es que el programador no tiene que enredarse con las cuestiones siempre espinosas de asignar o liberar memoria para los objetos creados, de forma que queda liberado de los operadores new y delete.

Filosofía de uso

Una primera pincelada sobre la filosofía de uso de los contenedores mediante un sencillo ejemplo: si tenemos que manejar un conjunto de enteros, puede utilizarse una matriz de enteros, pero ya hemos señalado que esta estructura nos obliga a conocer de antemano la cantidad de datos a almacenar (el tamaño de la matriz). En consecuencia, si el número de datos puede variar, esta opción no es muy adecuada. Además, si es importante mantener los datos ordenados, la utilización de una matriz nos obligaría a ingeniar por nuestra cuenta la forma de hacerlo; seguramente mediante complicadas rutinas de "sort" hechas manualmente.

Una alternativa es utilizar como "contenedor" para nuestros enteros una clase genérica de la STL denominada vector. En este caso, serían posibles expresiones como las siguientes:

 vector<int> v(3);         // L1:
 v[0] = 7;                 // L2:
 v[1] = v[0] + 1;          // L3:
 v[2] = v[0] + v[1];       // L4:

Al encontrar la primera sentencia, el compilador crea una instancia (especialidad) anónima de la plantilla vector para alojar enteros; a continuación una instancia de ella para alojar tres enteros, a la que denominamos v.

Una primera inspección de las sentencias L2 a L3, nos muestra que la clase ofrece la posibilidad de utilizar con sus miembros el operador subíndice de matriz [ ]. Deducimos por tanto, que dispone de una versión sobrecargada de este operador para los miembros de la clase, lo que nos permite utilizar el objeto v como una matriz, aunque con algunas funcionalidades no incluidas en aquellas.

Después de L4 los valores finales para los miembros de nuestra "matriz" serian:

v[0] == 7, v[1] == 8, v[2] == 15

Si deseamos invertir el orden de los elementos de nuestra matriz. Para ello utilizamos una función especial:

reverse(v.begin(), v.end());

después de lo cual, el orden de miembros sería:

v[0] == 15, v[1] == 8, v[2] == 7

La primera observación es que la función reverse utiliza dos argumentos, que son a su vez el valor devuelto por sendos métodos de clase (los aplicamos sobre nuestro objeto v). El valor devuelto por estos métodos es lo que se conoce como un iterador; una especie de puntero que señalan al interior de la estructura de datos representada por el contenedor v. En este caso, el primer iterador señala al primer miembro y el segundo a una posición después del último; de esta forma se indican los miembros inicial y final entre los que se realizará la operación de inversión efectuada por reverse.

La segunda observación es que la función reverse no es un método de clase, sino una función global; es lo que denominamos un algoritmo. Esta función tiene la peculiaridad de que puede ser aplicado sobre el contenedor vector (y algún otro); que opera sobre un rango de los elementos del contenedor, en nuestro caso sobre la totalidad (esta capacidad de operar sobre una parte de un contenedor es característica de los algoritmos). También que pueden operar sobre otros contenedores produciendo un efecto similar. Por ejemplo, el algoritmo reverse puede operar incluso sobre una simple matriz:

char Ch[5] = { 'A', 'E', 'I', 'O', 'U' };
reverse(Ch, Ch + 5);
for (int i = 0; i < 5; ++i) cout << "Ch[" << i << "] = " << Ch[i];
Salida:
Ch[0] = U Ch[1] = O Ch[2] = I Ch[3] = E Ch[4] = A

Es significativo que el objeto v es una estructura de datos que permite acceder individualmente a sus miembros "como si" fuesen elementos de una matriz. También que se pueden aplicar ciertas operaciones sobre la totalidad, o parte, de esta estructura considerada como una unidad.

Clasificación

Existen dos tipos de contenedores: secuencias y asociativos; a su vez se subdividen según el tipo de iterador que soportan.

Las secuencias almacenan los elementos en orden secuencial (de ahí su nombre). El contenedor agrupa a todos sus miembros como una sucesión lineal, y en consecuencia son adecuadas para accesos directos y secuenciales. Un ejemplo de secuencia es una matriz. De hecho, los algoritmos que pueden aplicarse a las secuencias pueden aplicarse también a las matrices.

Las asociaciones almacenan sus miembros en forma de árbol indexado, por lo que son denominados también contenedores asociativos ordenados, y resultan adecuados para accesos aleatorios mediante claves. La STL proporciona cinco tipos distintos.

La existencia de índices supone una ordenación según los valores de un conjunto de elementos (denominado "de claves") para el que debe existir un criterio de ordenación. Dicho en otras palabras, los objetos "de clave" deben ser de un tipo para el que estén definidos los operadores relacionales. El árbol es mantenido ordenado de forma automática por los algoritmos de inserción y borrado del contenedor. Existen mecanismos para asociar un valor o dato de cualquier tipo con una clave; para encontrar el valor asociado a una clave y para recorrer el conjunto (ordenado) de los elementos.

La Librería Estándar C++ contiene diez formas de contenedores y tres adaptadores de contenedor (denominados simplemente adaptadores). En esta sección se expondrán brevemente cuales son estas formas; sus características, y cuál es la más adecuada para cada tipo de problema específico. En secciones sucesivas se comentarán individualmente con mayor detalle. Relación siguiendo la clasificación utilizada en el Estándar:

 • Secuencias
     o	deque
     o	list
     o	stack
     o	vector
     o	vector <bool>
 • adaptadores
     o	queue
     o	priority_queue
 • Contenedores asociativos
     o	map
     o	multimap
     o	set
     o	multiset
     o	bitset

Características

Las características más significativas de las diversas formas de contenedor son:

Contenedor Características
Secuencias
vector Este contenedor es una estructura de datos de tamaño fijo preferentemente. Aunque la STL proporciona herramientas para cambiar el tamaño de un vector de forma dinámica, esta operación es costosa y debe ser evitada dentro de lo posible (en este sentido el vector se comporta como una matriz ordinaria).

Disponen de un buén mecanismo de acceso aleatorio a elementos y de un mecanismo de inserción al final muy eficiente.

Generalmente es preferible utilizar un vector que un deque o un list, a menos que sea frecuentemente necesario insertar datos al comienzo o al final, en cuyo caso es mejor utilizar un deque. Si por el contrario es frecuente la inserción de elementos en el centro, entonces es preferible un list.

vector<bool> Es una versión de la anterior especial para valores binarios (bits).
list Este contenedor responde a la idéa intuitiva de "lista", el almacenamiento de objetos en una secuencia lineal que no está necesariamente ordenada (aunque sus miembros pueden ser ordenados fácilmente mediante la función-miembro sort() ). Estos contenedores suelen ser implementados como listas doblemente enlazadas.

Dispone de mecanismos eficientes para insertar elementos al principio, al final o en cualquier posición (utilizando iteradores que denoten posición). Estas operaciones consumen un tiempo constante con independencia del número de elmentos albergados en el contenedor.

Dado que son estructuras lineales, en general los elementos no pueden ser accedidos por subíndices como en un vector. Es necesario realizar un recorrido lineal por todos los valores, por lo que en las operaciones de acceso se utilizan tiempos proporcionales al número de elementos.

stack Contenedor de elementos tipo pila LIFO que permite inserciones y eliminaciones solo en la parte superior.
deque Los deques o colas de doble terminación ("Double-ended queue") son un tipo de estructura de datos que comparte las características de las colas ("Queues") y las pilas ("Stacks"). Como en las colas , los elementos pueden ser empujados por un extremo al interior del contenedor, y el primer elemento introducido puede ser extraído por el extremo opuesto. Al mismo tiempo, el último elemento introducido por el principio puede ser extraído en ese mismo extremo como si fuese una pila.

Estos contenedores suelen ser implementados bajo la forma de matrices bidimensionales.

Las características de los deques implementados en la STL pueden resumirse en: Acceso aleatorio; mecanismo eficiente de inserción al principio o al final.

string Contenedor de caracteres adaptado a operaciones con cadenas de caracteres.
Asociaciones
set Como el resto de contenedores asociativos, esta forma de contenedor mantiene los elementos en orden. Dispone de mecanismos eficientes para inclusión, inserción y eliminación de elementos, y soporta claves únicas (solo puede existir un miembro con una clave determinada).
multiset Versión del anterior que permite la existencia de claves duplicadas. Es decir, distintos elementos dentro del conjunto pueden responder a la misma clave.
bitset Contenedor de bit más orientado al tamaño que hacia el tipo de contenido. Permite almacenar secuencias de bits de tamaño fijo. No existen iteradores para reccorrerlas, y sus elementos se acceden utilizando el operador subíndice.

Se dispone de varias funciones para realizar con ellos operaciones de bits

map Como el resto de estructuras asociativas, el map mantiene sus elementos ordenados. Se caracteriza porque sus miembros son pares de valores que pueden ser de tipos distintos. Uno de ellos, el que actúa como clave para el índice ("Key-value"), puede ser de cualquier tipo, a condición de que sus elementos puedan ser ordenados según un criterio (por defecto se utiliza el operador <).

Permite claves únicas. Es decir, que solo puede existir un miembro para cada clave. Dispone de mecanismos de inserción y borrado muy eficientes y no existe límite de tamaño. La estructura se encarga de crecer y disminuir en concordancia con las necesidades de sus miembros. Permite el operador [ ] subíndice para los elementos de la clave así como otras técnicas de acceso.

Estas estructuras de datos recibe también los nombres de diccionarios, tablas y matrices asociativas, en referencia a que pueden considerarse como dos matrices del mismo número de elementos, en las que existe una relación entre cada miembro de una con otro miembro de la otra. Una de las matrices (que actúa de índice) estaría ordenada según su contenido

multimap En todo igual que el anterior pero permitiendo además claves duplicadas. Es decir, que una misma clave pueda estar asociada a dos "valores" distintos.
Adaptador Características
queue Contenedor tipo cola FIFO que permite inserciones al final y eliminaciones al principio.
priority queue Este contenedor dispone de mecanismos eficientes para acceso y eliminación de grandes valores.

Seleccionar un contenedor

El binomio contendor-iterador conforma una herramienta que se adapta a circunstancias muy variadas. Es posible que un determinado problema pueda ser abordado de varias formas, en estos casos la selección de la forma de contenedor más adecuado depende de las circunstancias. En ciertas ocasiones puede resultar bastante obvia; en otras puede no estar tan claro cuál es la mejor opción, pues "a priori", pueden adaptarse varias formas al caso concreto. Puede utilizarse el sistema de prueba y comparación. Por ejemplo, cronometrando los resultados obtenidos en un modo u otro. También será de gran ayuda la experiencia, pero como una guía o criterio general puede utilizarse el principio de selección que se indica a continuación.

Como se accederá a los datos?. Si es importante un acceso aleatorio, pueden utilizarse un vector o un deque. Si basta con un acceso secuencial cualquiera de los dos puede ser el elegido.
Es importante el orden en que serán mantenidos los valores dentro de la colección de datos? Existen muchas formas de secuenciar los valores. Si es importante un orden estricto a lo largo de toda la vida del contenedor, la elección obvia es un set, dado que las inserciones en una de estas estructuras se realizan automáticamente en orden.

Si este orden es importante solo en algún momento, por ejemplo al final de una serie de inserciones, puede ser más sencillo colocar los valores en un list o vector para ordenar más tarde el resultado en el momento necesario.

Si el orden en que se almacenan los valores en la estructura está relacionado con el orden de su inserción, entonces un stack, un queue o un list pueden ser la mejor elección.

Puede variar mucho el tamaño de la estructura de datos durante el curso de la ejecución del programa? Si es así, entonces puede ser que la mejor opción sean un list o un set. Un vector o un deque pueden conducir a mantener un gran almacenamiento intermedio (buffer); incluso después que los elementos hayan sido eliminados de la colección de datos. Estos dos contenedores no son adecuados para cambios de tamaño, aunque vector dispone de un mecanismo de inserción al final eficiente.

Recíprocamente: si el tamaño de la colección se mantiene relativamente estable, entonces un vector o un deque pueden utilizar menos memoria que un list o un set que almacenen el mismo número de elementos.

Es posible determinar el tamaño de la colección? La estructura de datos vector proporciona un medio para pre-seleccionar un bloque de memoria de un tamaño específico con la función miembro reserve, posibilidad que no existe en otros contenedores.
Es frecuente tener que comprobar si un valor cualquiera está incluido en la colección? En este caso, un contenedor set o map pueden ser buena elección. En estos casos comprobar si un determinado valor está incluido puede ser realizado con un pequeño número de pasos (logarítmicos con el tamaño del contenedor). Mientras que la misma verificación en cualquiera de los otros tipos puede requerir comparar el valor con cada elemento almacenado en el contenedor.
Está indexada la colección de datos? Si las claves son enteros entre 0 y un límite superior, pueden utilizarse un vector o un deque.
Si es así, puede ser vista la colección como una serie de pares clave/valor? Si la clave es algún otro valor, como cadenas de caracteres o tipos definidos por el usuario, puede utilizarse un contenedor map.
Pueden los valores estar relacionados unos con otros? Todos los valores almacenados en cualquier contenedor de la STL debe ser capaz de comprobar la igualdad con cualquier otro valor similar, pero recuerde que no todos son capaces de reconocer el operador de relación menor-que; sin embargo, si los valores no pueden ser ordenados utilizando el operador menor-que, no pueden ser almacenados en un set o en un map.
Es una operación frecuente tener que encontrar y eliminar el valor mayor de la colección? En caso afirmativo, la estructura de datos más idónea es el priority queue.
En que posiciones son insertados o eliminados los elementos de la estructura? Si los valores son añadidos o eliminados en la zona interior, entonces la mejor opción es un list.

Si los valores son insertados solo al comienzo, un deque o un list pueden ser lo más adecuado.

Si los valores son insertados y eliminados solo al final, un stack o un queue pueden ser elecciones lógicas.

Es frecuente tener que refundir dos o más secuencias en una sola? En caso afirmativo, puede considerarse que las mejores opciones son un set o un list, dependiendo que la colección deba o no ser mantenida en orden.

Refundir dos set es una operación muy eficiente. Si la colección de datos no está ordenada, pero puede utilizarse la función miembro splice() de las list (que es muy eficiente), entonces es preferible este último tipo de contenedor, dado que esta operación no está contemplada en las demás formas.

Tipos no incluidos

La Librería Estándar C++ incluye contenedores adecuados para la mayoría de los casos prácticos, aunque quizás existen una serie de estructuras de datos clásicas que no han sido incluidas. En la mayoría de los casos, la causa es que los contenedores proporcionados pueden ser fácilmente adaptados para suplir los usos facilitados por los tipos omitidos.

Tipos clásicos no incluidos y la sustitución más fácil.

Contenedor omitido Sustitución utilizando elementos de STL
tree El modo set está implementado internamente como un árbol de búsqueda binaria.

Para la mayoría de los casos prácticos que pueden ser resueltos utilizando árboles este modo es un sustituto adecuado.

matrices multidimensionales Puesto que los vectores pueden almacenar elementos que sean otros vectores, estas estructuras pueden ser construidas fácilmente.
graph La representación de un gráfico puede ser fácilmente construida como un map que contiene otros map. Un map cuyos elementos son a su vez maps, es la representación natural de un gráfico directo.
sparse array Una novedosa sustitución es la representación de un gráfico
hash table Las tablas hash proporcionan un tiempo de acceso constante, así como inserción y borrado de elementos, convirtiendo estas operaciones en operaciones de índice.

Una tabla hash puede ser construida fácilmente como un vector (o deque) cuyos elementos son lists (o incluso sets). En el ejemplo de la ordenación raíz ( xxx) se describe una de estas estructuras (un vector de deques), aunque en este ejemplo no incluye invocación la función hash para convertir un valor en un índice.

Funcionalidad set En la STL, los tipos set están específicamente ordenados, y sus operaciones (como la unión intersección, etc.) no pueden ser realizadas sobre colecciones de valores no ordenados (por ejemplo un conjunto de números complejos).

Una list puede ser utilizada como sustituto, aunque es necesario escribir funciones de operación especiales, dado que los algoritmos genéricos no pueden ser utilizados con listas.

Gestión de memoria

La gestión de memoria realizada por los contenedores comprende tanto la asignación y liberación de espacio para sus miembros, como para las claves que constituyen el sistema de índice de algunos tipos de contenedor. En general los algoritmos de la STL están muy optimizados y el sistema de asignación de memoria intenta compaginar rapidez y eficacia. La primera exige asignar y desasignar espacio cada vez que se introduce o elimina un miembro del contenedor. La eficacia, e incluso la disminución de la fragmentación inherentes al manejo del montón, exige asignar y rehusar trozos de memoria lo más grandes posible cada vez. El abordaje a este problema se ha realizado de dos formas básicas: (a) Contenedores que no pueden alterar su tamaño, de forma que la asignación de espacio para sus elementos debe realizarse en el momento de su declaración. (b) Contenedores que pueden crecer y/o disminuir en consonancia con los elementos a albergar.

Refiriéndonos al caso (b), para balancear estas exigencias contrapuestas, algunas implementaciones utilizan el siguiente esquema de trabajo: inicialmente el sistema asigna al contenedor un trozo de memoria de tamaño adecuado para contener cierto número de elementos; espacio que es utilizando hasta que se agota. A partir de aquí, cada nueva demanda origina la asignación una zona de doble tamaño que la anterior. Por ejemplo, supongamos que para gestionar un vector de enteros, el compilador asigna inicialmente espacio para un máximo de 16 miembros. Cuando se agotara, se realizaría una nueva asignación de espacio para 32 miembros, e inmediatamente se efectuaría una recolocación del contenido actual en el nuevo espacio y la liberación del primitivo. Si el nuevo contenedor quedara lleno de nuevo, la siguiente asignación sería capaz de 64 miembros y así sucesivamente.

El factor de aumento de tamaño puede ser cualquiera. Muchos compiladores utilizan un factor 1.5 cada vez que deben ampliar el espacio necesario.

Cada nueva recolocación implica varios pasos:

 •	Asignación de memoria al estilo malloc() de la librería clásica en cantidad suficiente para el nuevo tamaño.
 •	Copia de los elementos actuales en la nueva localización. Los elementos son colocados en la nueva situación utilizando el constructor copia.
 •	Actualización de las estructuras internas del nuevo contenedor (sistema de índices).
 •	Eliminación de los elementos originales mediante invocación sucesiva de sus correspondientes destructores.
 •	Liberación de la memoria utilizada por las estructuras internas.
 •	Liberación de la zona original mediante una invocación al estilo free de la librería clásica.

En general, al referirse al tamaño de un contenedor existen tres magnitudes distintas aunque relacionadas, cuyos valores pueden obtenerse mediante tres métodos:

 •	El número de elementos contenidos actualmente. size()
 •	El máximo número de elementos que pueden alcanzarse actualmente sin necesidad de una nueva recolocación. capacity()
 •	El máximo número de elementos que pueden alcanzarse en cualquier caso. max_size().
Ejemplo:
  #include <iostream>
  using namespace std;
  #include <vector>
  int main() {         // ================
  vector<int> vInt;
  for (int i=0; i < 100; i++) vInt.push_back(i);
  cout << "Tamaño actual:   " << vInt.size()     << endl;
  cout << "Capacidad actual: " << vInt.capacity() << endl;
  cout << "Tamano maximo:   " << vInt.max_size() << endl;
  return 0;
  }
  Salida:
  Tamaño actual: 100
  Capacidad actual: 256
  Tamano maximo: 1073741823

En todos los casos: size() <= capacity()

Es posible comprobar si un contenedor está vacío, comparando su tamaño actual size() con cero, aunque es más rápido utilizar el método empty(), que devuelve true si realmente size() == 0.

Reserva de espacio

Como puede suponer, cada recolocación supone un proceso considerable, sobre todo en contenedores muy grandes. Para evitar sus inconvenientes si se conoce de antemano el tamaño máximo a utilizar, la STL ofrece la posibilidad de preasignar un espacio arbitrario. Para esto se dispone del método reserve(n), que garantiza la asignación de espacio inicial suficiente para alojar n miembros en el contenedor. Si el argumento n es mayor que la capacidad actual, se produce una recolocación y el nuevo argumento determina la nueva capacidad. Si es menor no se produce ninguna acción.

Ejemplo, para crear un vector de enteros capaz para al menos 1000 miembros:

 vector <int> v;
 v.reserve(1000);

En el siguiente ejemplo se muestran los resultados compilado con Borland C++ 5.5 para Win32; con MS Visual C++ 6.0 y con GNU gcc 2.95.3 para Linux:

 // resultados con:         BC++   VC++   GNU   
 vector<int> v;
 cout << v.size();      // -> 0      0      0   
 cout << v.capacity();  // -> 0      0      0
 v.push_back(3);
 cout << v.size();      // -> 1      1      1
 cout << v.capacity();  // -> 256    1      1
 v.reserve(100);
 cout << v.size();      // -> 1      1      1
 cout << v.capacity();  // -> 256    100    100

Operaciones sobre contenedores

Los elementos de un contenedor son colocados en él utilizando el constructor copia, aunque algunas operaciones requieren la existencia de un constructor por defecto. A su vez, los algoritmos genéricos, como copy(), que copian a un contenedor, utilizan el operador de asignación.

Cuando se duplica todo un contenedor de tipos T por invocación del constructor copia o por asignación, cada miembro es copiado en la nueva estructura utilizando el constructor copia o el operador de asignación. Tanto si una copia profunda o una copia somera, el resultado es controlado por el programador, que puede proporcionar el operador de asignación para el tipo T con cualquier significado que desee. Si se ha definido un destructor para el tipo T, este destructor es invocado cada vez que deba ser eliminado un elemento del contenedor. Cuando se destruye toda la colección, el destructor es invocado por cada elemento que permaneciera almacenado.

En estos casos, el contenedor es solamente responsable de mantener los propios punteros, siendo responsabilidad del programador manejar la memoria de los valores referenciados por estos, lo que supone que los espacios de memoria son asignados correctamente; por lo general mediante el operador new; que no son desasignados mientras el contenedor albergue referencias a ellos, y que sean adecuadamente desasignados una vez que han sido eliminados del contenedor.

Secuencias

Las secuencias son estructuras de datos de tipo lineal, con posibilidad de que su tamaño puede ser alterado insertando elementos al principio, al final o en cualquier posición, si bien la idoneidad en este sentido es distinta entre los diversos tipos.

La STL ofrece los siguientes:

 • deque
 • list
 • stack
 • vector
 • vector<bool>

La estructura lineal implica que sus datos son lógicamente contiguos, aunque su disposición física puede que no lo sea. Los elementos pueden ser accedidos por un índice numérico, aunque en algunos tipos de secuencia el acceso a un elemento implica un recorrido por la lista.

Contenedores asociativos

Los contenedores asociativos son una extensión de las secuencias. Mientras que en estas para acceder a los elementos solo pueden utilizarse claves numéricas, que señalan el número de orden del elemento dentro de la secuencia (al estilo de los índices en las matrices). Los contenedores asociativos admiten cualquier tipo de clave de acceso. Las más frecuentes son las cadenas alfanuméricas (stings).

Las asociaciones almacenan sus miembros en forma de árbol indexado, razón por la que son denominados también contenedores asociativos ordenados, y resultan adecuados para accesos aleatorios mediante claves.

La ordenación se realiza según los valores de ciertos miembros del contenedor. Estos elementos son denominados "de claves" ("Key values"). Lo que exige que los objetos "de clave" sean de un tipo para el que estén definidos los operadores relacionales. El contenedor es mantenido ordenado de forma automática por los algoritmos de inserción y borrado.

La STL proporciona cinco tipo distintos, aunque en realidad son tres clases; dos de ellas en dos variedades (permitiendo o no claves duplicadas). Uno de los tipos (bitset) sirve para almacenar campos de bits de longitud constante. Los otros dos permiten elementos ordenados de cualquier tipo (set y multiset), mientras que map y multimap permiten alojar pares de elementos. Uno que actúa como "Key value" y otro que puede ser un tipo cualquiera al que llamaremos valor-dato.

 • bitset
 • set
 • multiset
 • map
 • multimap

Existen mecanismos para asociar un valor o dato de cualquier tipo con una clave; para encontrar el valor asociado a una clave, y para recorrer el conjunto (ordenado) de las claves.

Véase también

Fuente