2011-12-19 14 views
7

Digamos que creo una lista enlazada en AWL:¿Los asignadores personalizados en STL solo asignan los datos reales?

list<int, my_allocator<int> > data; 

entonces puedo utilizar un asignador más eficiente, digamos un banco de memoria. ¿Pero la lista no necesita asignar memoria interna como punteros hacia adelante y hacia atrás para atravesar la lista? ¿Cómo se asignarán? ¿Usando normal new o de alguna manera usando my_allocator?

+0

¿Te refieres a la implementación de la Biblioteca estándar de STL o C++ que se envía con tu cadena de herramientas elegida? ¿Y cuál es ese? –

+0

@ TomalakGeret'kal, ¿qué? (+1 a la pregunta) – avakar

+0

@avakar El STL fue originalmente un proyecto sgi. Desde entonces ha habido varias implementaciones de STL (como STLport) y gran parte de ella se ha agregado a la biblioteca estándar según lo define el comité, aunque con algunos cambios. Todo lo cual deja * * STL * * un poquito ambiguo en formas que pueden afectar esta pregunta. – dmckee

Respuesta

10

El contenedor reutiliza su asignador para asignar su propio material de contabilidad. (No es que importa para un std::list, pero es cierto en general *.) Esta es la razón por los requisitos asignador estándar exigen la existencia de la plantilla rebind:

typedef typename Alloc::template rebind<MyInternalStuff>::other internal_allocator; 

Si el asignador es Alloc = my_allocator<T>, entonces internal_allocator convierte my_allocator<MyInternalStuff> .

Creo que esta fue una de las quejas que Electronic Arts tuvo con la biblioteca estándar de C++, razón por la cual su biblioteca EASTL usa una convención diferente para asignadores que ofrece un control más estricto.

*) Normalmente, cada nodo será un objeto monolítico de algún tipo Node<T>, así que supongo std::list<T, Alloc>solamente nunca utiliza Alloc::rebind<Node<T>>::other como asignador.

[Disculpa las múltiples ediciones; Tuve la salida destrozada y no la interpreté correctamente; Ahora fui e imprimí cada contenedor por separado y arreglé la salida en consecuencia. std::list en efecto, sólo requieren una asignador]


Actualización:. Sólo por diversión, escribí un poco de desenredo-asignador que imprime su propio nombre de tipo en la construcción. Aquí está la entrada:

#include <unordered_map> 
#include <set> 
#include <deque> 
#include <list> 
#include <vector> 
#include <map> 

#include <iostream> 

int main() 
{ 
    std::cout << "----- unordered_map<int, double> -----------" << std::endl; 
    std::unordered_map<int, double, std::hash<int>, std::equal_to<int>, funky_allocator<std::pair<const int, double>>> m { {1, 1.2} }; 
    std::cout << "----- set<int> -----------------------------" << std::endl; 
    std::set<int, std::less<int>, funky_allocator<int>> s; 
    std::cout << "----- deque<int> ---------------------------" << std::endl; 
    std::deque<int, funky_allocator<int>> d; 
    std::cout << "----- list<int> ----------------------------" << std::endl; 
    std::list<int, funky_allocator<int>> l; 
    std::cout << "----- vector<int> --------------------------" << std::endl; 
    std::vector<int, funky_allocator<int>> c; 
    std::cout << "----- map<int, bool> -----------------------" << std::endl; 
    std::map<int, bool, std::less<int>, funky_allocator<std::pair<const int, bool>>> n { { 1, true } }; 
} 

Y aquí el resultado:

----- unordered_map<int, double> ----------- 
Default-construct: funky_allocator<std::pair<int const, double> > 
Copy-construct: funky_allocator<std::__detail::_Hash_node<std::pair<int const, double>, false> > 
Copy-construct: funky_allocator<std::__detail::_Hash_node<std::pair<int const, double>, false>*> 

----- set<int> ----------------------------- 
Default-construct: funky_allocator<std::_Rb_tree_node<int> > 

----- deque<int> --------------------------- 
Default-construct: funky_allocator<int> 
Copy-construct: funky_allocator<int*> 

----- list<int> ---------------------------- 
Default-construct: funky_allocator<std::_List_node<int> > 

----- vector<int> -------------------------- 
Default-construct: funky_allocator<int> 

----- map<int, bool> ----------------------- 
Default-construct: funky_allocator<std::_Rb_tree_node<std::pair<int const, bool> > > 

Los detalles varían en función de la cual se utiliza el constructor: Contenedores como set y map sólo se podría construir el asignador "correcta" de alguna invocación, mientras que en otro pueden construir un objeto del asignador especificado primero. De cualquier manera, el asignador especificado nunca se usa en absoluto para un par de contenedores, y solo se utiliza la versión de rebote.

+0

Gracias por saber qué;) –

+0

+1, aunque te falta 'plantilla' antes de' rebind' :) – avakar

+0

@avakar: ¡Tienes razón, me acabo de dar cuenta de eso! De hecho, acabo de escribir mi propio asignador de demanda que imprime su propio nombre de clase en la creación de instancias para verificar cuántos asignadores diferentes requieren algunos contenedores populares. –

Cuestiones relacionadas