Habida
cuenta que la misión principal de los contenedores es el almacenamiento de
objetos, resulta natural que entre sus funciones más importantes, se encuentre
la gestión del espacio necesario para albergarlos. Esta gestión no se limita a
asignar y liberar el espacio necesario para sus miembros. En ocasiones también
debe gestionar espacio para la estructura de índices que mantiene el orden de
la secuencia. Por ejemplo, cuando añadimos miembros a un contenedor tipo vector, este se redimensiona automáticamente
conforme se van añadiendo nuevos elementos. Análogamente, cuando añadimos
nuevos elementos a un map este
acomodo afecta a los nuevos elementos y a las claves asociadas que constituyen
su sistema de índice.
Como
se ha dicho, todas las entidades de la STL que precisan de memoria dinámica lo
hacen a través de los "servicios" de una clase "asignadora"
que es utilizada como argumento (recuerde que las entidades de la STL son
plantillas que aceptan distintos parámetros). Debido a que además de portable,
la STL es extensible, existe libertad para que el usuario implemente su propio
asignador de memoria; aunque la STL proporciona un gestor por defecto, la
clase allocator, que
implementa la funcionalidad requerida mediante la utilización de los operadores
estándar new y delete . En caso de no
indicarse otro explícitamente, se utiliza este asignador por defecto, que suele
ser suficiente en la mayoría de los casos. El resultado es que el usuario puede
despreocuparse de la cuestión, y que los asignadores son ráramente utilizados
de forma explícita. Por ejemplo, la definición del contenedor vector es del siguiente tenor:
espacio de nombres
std {
template <class T, asignador class = asignador <T>> clase
vector
pública:
...
};
}
Desde
la perspectiva del usuario de un contenedor estándar, el manejo de memoria es
realizado automáticamente mediante el parámetro de moldeo. Por ejemplo,
para definir una lista de enteros myList utilizando el asignador por defecto,
puede utilizarse el parámetro <int> y escribir:
# Include <Memoria>
...
Lista
<INT> mi lista;
En la
declaración del contenedor también puede proporcionarse un asignador específico
mediante un segundo parámetro de moldeo. Por ejemplo, para utilizar en la lista
anterior un asignador propio denominado fastAllocator,
que suponemos está pensado para tipos int, y definido en el fichero de cabecera <fastAllocator.h>, utilizaríamos
la definición siguiente:
#include
<memory>
#include
<fastAllocator.h>
…
tipedef
list <int, fastAllocator> myList
También podría utilizarse el asignador por defecto de forma explícita:
# include
<memory>
…
allocator<int>
miAllocint;
list
<int, miallocint> mylist;
Como
puede verse, cuando se instancia una especialización concreta de un contenedor
genérico, debe especificarse el tipo de miembro que alojará en el contenedor y
el asignador de memoria que utilizara. Posteriormente, cuando se instancie
un objeto del tipo myList, se
especifica el objeto-asignador que utilizará el contenedor:
fastAllocator
asignadorl;
mylist
listal (asignadorl);
El manejador por defecto:
Las definiciones de los
asignadores de la STL están en el espacio de nombres std y se encuentran agrupadas en la
cabecera <memory>. La STL
proporciona un manejador por defecto denominado allocator.
Esta clase es en realidad una plantilla que puede instanciarse para manejar
cualquier tipo. Responde a la siguiente interfaz:
Por supuesto que la
clase satisface las premisas indicadas por el Estándar para ser un allocator por
lo que es un asignador estándar. También se proporciona una instanciación para
el tipo voidque responde a la siguiente interfaz:
Ejemplos
vector<double> V(100,
5.0); // Usa el asignador por defecto
vector<double, single_client_alloc> local(V.begin(), V.end());
vector<double, single_client_alloc> local(V.begin(), V.end());
No hay comentarios:
Publicar un comentario