This is a good article. Click here for more information.

Asignador (C++)

Ir a: navegación, búsqueda de

En C++ programación informática, asignadores son un componente importante de la Biblioteca estándar de C++. El estándar Biblioteca proporciona varios estructuras de datos, tales como lista y conjunto, comúnmente conocida como contenedores. Un rasgo común entre estos contenedores es su capacidad para cambiar el tamaño durante la ejecución de la programa. Para lograr esto, alguna forma de asignación de memoria dinámica es generalmente necesaria. Asignadores de manejar todas las solicitudes de asignación y desasignación de memoria para un contenedor determinado. La biblioteca estándar de C++ proporciona asignadores de propósito generales que se utilizan de forma predeterminada, sin embargo, asignadores personalizados también pueden ser suministrados por el Programador.

Asignadores fueron inventados por Alexander Stepanov como parte de la Biblioteca de plantillas estándar (STL). Originalmente fueron pensados como un medio para crear la biblioteca más flexible e independiente del subyacente modelo de memoria, permitiendo a los programadores utilizan personalizado puntero y referencia tipos con la biblioteca. Sin embargo, en el proceso de adopción de STL en el Estándar C++, el Comité de normalización de C++ que se dio cuenta de una completa abstracción de la memoria modelo incurriría inaceptable rendimiento sanciones. Para remediar esto, los requisitos de calefacción se hicieron más restrictivos. Como resultado, el nivel de personalización mediante distribuidores es más limitado que fue originalmente concebida por Stepanov.

Sin embargo, hay muchos escenarios donde asignadores modificado para requisitos particulares son deseables. Algunas de las razones más comunes para escribir asignadores personalizados incluyen mejorar el rendimiento de las asignaciones mediante el uso de piscinas de memoriay encapsula el acceso a diferentes tipos de memoria, como memoria compartida o recolección memoria. En particular, los programas con muchas asignaciones frecuentes de pequeñas cantidades de memoria pueden beneficiarse grandemente de distribuidores especializados, tanto en términos de tiempo de funcionamiento y huella de la memoria.

Contenido

  • 1 Fondo
  • 2 Requisitos
  • 3 Asignadores de encargo
    • 3.1 Uso
    • 3.2 Mejoras para distribuidores en C ++ 11
  • 4 Ejemplo
  • 5 Referencias
  • 6 Enlaces externos

Fondo

Para obtener más información: Biblioteca de plantillas estándar

Alexander Stepanov y Meng Lee presentó el Biblioteca de plantillas estándar para el C++ Comité de estándares en marzo de 1994.[1] La biblioteca recibió aprobación preliminar, aunque algunos temas fueron levantados. En particular, Stepanov fue solicitado para hacer los contenedores biblioteca independiente del subyacente modelo de memoria,[2] que condujeron a la creación de distribuidores. En consecuencia, todas las interfaces de contenedor STL tuvieron que ser reescrita para aceptar asignadores.

En la adaptación de STL para ser incluidos en el Biblioteca estándar de C++, Stepanov trabajó estrechamente con varios miembros del Comité de estándares, incluyendo Andrew Koenig y Bjarne Stroustrup, quien observó que potencialmente podrían utilizarse asignadores personalizados para implementar almacenamiento persistente Contenedores STL, que Stepanov en el momento considerado "una penetración importante e interesante".[2]

Desde el punto de vista de la portabilidad, todas las cosas específicas que se refieren a la noción de dirección, puntero y así sucesivamente, se encapsulan en un mecanismo de pequeño, bien entendido. [2]

—Alex Stepanov, diseño de la Biblioteca de plantillas estándar

La propuesta original de asignador incorpora algunas características del lenguaje que aún no habían sido aceptadas por el Comité, a saber: la capacidad de utilizar argumentos de plantilla son las plantillas. Puesto que estas características no podían ser compiladas por cualquier existentes compilador, había, según Stepanov, "una enorme demanda de Bjarne [Stroustrup] y Andy tiempo de [Koenig] tratando de verificar que estamos usando estas características no implementadas correctamente."[2] Donde previamente había utilizado la biblioteca puntero y referencia tipos directamente, se ahora sólo referiría a los tipos definidos por el asignador. Stepanov describió más tarde asignadores como sigue: "una característica interesante de STL es el único lugar que menciona los tipos relacionados con la máquina (...) está encapsulado dentro de aproximadamente 16 líneas de código".[2]

Mientras Stepanov había previsto originalmente asignadores de encapsular completamente el modelo de memoria, el Comité de estándares se dio cuenta de que este enfoque conduciría a las degradaciones de rendimiento inaceptable.[3][4] Para remediar esto, los requisitos de asignador añadido texto adicional. En particular, las implementaciones de envase pueden asumir que del asignador definiciones de tipo para punteros y afines tipos integrales son equivalentes a las ofrecidas por el asignador predeterminado y que todos instancias de un tipo determinado asignador comparar siempre igual,[5][6] contradiciendo con eficacia los objetivos originales del diseño para distribuidores y limitando la utilidad del distribuidor de los que llevan el estado.[4]

Stepanov después comentó que, mientras que los distribuidores "no son tan mala [idea] en teoría (...) [u] desgracia que no pueden funcionar en la práctica". Observó que para hacer asignadores realmente útil, un cambio en el lenguaje con respecto a referencias era necesario.[7]

El 2011 revisión del estándar C++ eliminado el palabras de la comadreja requiriendo que asignadores de un tipo dado siempre comparar iguales y utilizan punteros normales. Estos cambios hacen asignadores stateful mucho más útil y permitan asignadores de gestionar memoria compartida out-of-process.[8][9] El objetivo actual de distribuidores es darle el control programador de asignación de memoria dentro de contenedores, más que adaptar el modelo de dirección del hardware subyacente. De hecho, la norma revisada elimina la capacidad de calefacción para representar las extensiones del modelo de dirección de C++, formalmente (y deliberadamente) eliminando su propósito original.[10]

Requisitos

Cualquier clase que cumple la requisitos de asignador puede ser utilizado como un asignador. En particular, una clase A capaz de asignar memoria para un objeto de tipo T debe proporcionar los tipos A::Pointer, A::const_pointer, A::Reference, A::const_reference, y A::value_type para declarar genéricamente objetos y referencias (o sugerencias) a objetos de tipo T. Asimismo deberá aportar tipo A::size_type, un tipo sin signo que puede representar el mayor tamaño de un objeto en el modelo de asignación definido por Ay del mismo modo, una firmada integral A::difference_type Eso puede representar la diferencia entre dos punteros en el modelo de asignación.[11]

Aunque se permite una implementación biblioteca estándar conforme a suponer que del asignador A::Pointer y A::const_pointer son simplemente definiciones de tipos para T* y T const *, implementadores de biblioteca son alentados a apoyar asignadores más generales.[12]

Un asignador, A, para los objetos de tipo T debe tener una función miembro con la firma A::Pointer A::allocate (size_type n, un vacío < >:: const_pointer pista = 0). Esta función devuelve un puntero al primer elemento de una matriz recién asignado suficientemente grande para contener n objetos de tipo T; Sólo la memoria se asigna, y los objetos no son construidos. Por otra parte, un argumento de puntero opcional (que apunta a un objeto ya asignado por A) puede ser utilizado como una sugerencia para la aplicación sobre dónde debería asignarse la nueva memoria para mejorar localidad.[13] Sin embargo, la aplicación es libre de ignorar el argumento.

El correspondiente void A::deallocate (A::pointer p, A::size_type n) función miembro acepta cualquier puntero que fue devuelto desde una invocación anterior de la A::allocate función miembro y el número de elementos para desasignar (pero no destrucción).

El A::max_size() función miembro devuelve el número más grande de objetos de tipo T Eso era de esperarse que se asignen con éxito por una invocación de A::allocate; el valor devuelto es típicamente A::size_type(-1) / sizeof(T).[14] Además, la A::Address función miembro devuelve un A::Pointer denotando la dirección de un objeto, dada una A::Reference.

Destrucción y construcción del objeto se realiza por separado de asignación y desasignación.[14] El asignador es necesario tener dos funciones miembro, A::Construct y A::Destroy, que encarga de construcción de objeto y destrucción, respectivamente. La semántica de las funciones debe ser equivalente a lo siguiente:[11]

plantilla <TypeName T>
vacío A::construir(A::puntero p, A::const_reference t) { Nuevo ((vacío*) p) T(t); }
 
plantilla <TypeName T>
vacío A::destruir(A::puntero p){ ((T*)p)->~T(); }

El código anterior utiliza el colocación Nuevo sintaxis y pide la Destructor directamente.

Deben ser asignadores copia-construible. Un asignador de objetos de tipo T pueden ser construidos de un asignador de objetos de tipo U. If un asignador, A, asigna una región de memoria, R, entonces R Sólo puede ser elimina por un asignador que compara igual a A.[11]

Distribuidores están obligados a proporcionar a un miembro de la clase de plantilla plantilla < typename U > struct A::rebind {typedef A < U > otros;};, que permite la posibilidad de obtener un asignador relacionado, parametrizados en cuanto a un tipo diferente. Por ejemplo, dado un tipo asignador IntAllocator para los objetos de tipo int, un tipo de asignador relacionados para objetos de tipo largo puede obtenerse usando IntAllocator::rebind < largo >:: otros.[14]

Asignadores de encargo

Una de las principales razones para escribir un asignador personalizado es rendimiento. Utilizando un asignador personalizado especializado puede mejorar sustancialmente el rendimiento o uso de la memoria o ambas, del programa.[4][15] El asignador predeterminado utiliza nuevo operador asignar memoria.[16] Esto a menudo es implementado como una capa fina alrededor de la C pila funciones de asignación,[17] que generalmente están optimizados para infrecuente asignación de bloques de memoria de gran capacidad. Este enfoque puede funcionar bien con contenedores que en su mayoría destinar grandes cantidades de memoria, como Vector y deque.[15] Sin embargo, para los envases que requieren frecuentes asignaciones de objetos pequeños, tales como mapa y lista, usando el asignador predeterminado es generalmente lenta.[4][17] Otros problemas comunes con un malloc-base asignador incluyen pobres localidad de referencia,[4] y excesivo fragmentación de la memoria.[4][17]

Un enfoque popular para mejorar el rendimiento es crear un agrupación de memoria-base asignador.[15] En lugar de asignar la memoria cada vez que un elemento es insertado o retirado de un recipiente, un gran bloque de memoria (la piscina de la memoria) es asignado de antemano, posiblemente en la puesta en marcha del programa. El asignador personalizado servirán las solicitudes de asignación individual por simplemente devolver un puntero a la memoria de la piscina. Real desasignación de memoria puede ser diferida hasta la toda la vida de los extremos del pool de memoria. Un ejemplo de asignadores de memoria basada en la piscina puede encontrarse en el Bibliotecas Boost C++.[15]

Otro uso viable de asignadores de encargo es para depuración errores relacionados con la memoria.[18] Esto podría lograrse escribiendo un asignador que asigna memoria extra en el que pone información de depuración.[19] Tan un Asignador podría utilizarse para garantizar que la memoria es asignada y elimina por el mismo tipo de asignador y también proporcionar una protección limitada contra desbordes.[19]

En definitiva, este párrafo (...) es el estándar "Yo tengo un sueño"discurso para distribuidores. Hasta que ese sueño se convierte en realidad común, programadores preocupado por portabilidad se limitan a asignadores personalizados sin estado

—Scott Meyers, Eficaz STL

El tema de la aduana asignadores ha sido tratado por muchos C++ expertos y autores, incluyendo Scott Meyers en STL eficaz y Andrei Alexandrescu en Moderno diseño de C++. Meyers destaca que C ++ 98 requiere que todos instancias de un asignador equivalente y observa que en efecto esto obliga portable asignadores de no tener estado. Aunque la norma 98 C ++ alentar implementadores de biblioteca para apoyar con distribuidores,[12] Llamadas Meyers el correspondiente apartado "linda" que "le ofrece casi nada", caracterizando la restricción como "draconiana".[4]

En El lenguaje de programación C++, Bjarne Stroustrup, por el contrario, sostiene que el "aparentemente [d] raconian restricción contra información por objeto en distribuidores no es particularmente grave",[3] señalando que la mayoría de distribuidores no necesita del estado y tienen un rendimiento mejor sin él. Él menciona que tres casos de uso para asignadores de encargo, a saber, agrupación de memoria distribuidores, memoria compartida distribuidores, y memoria de basura recogida asignadores. Presenta una implementación asignador que utiliza un pool de memoria interna para la rápida asignación y desasignación de pequeños pedazos de memoria, pero que esas notas un optimización Ya se puede realizar por el asignador proporcionado por la aplicación.[3]

Uso

Al crear instancias de uno de los contenedores estándar, el asignador se especifica mediante un plantilla argumento, que valores predeterminados Para STD::Allocator < T >:[20]

espacio de nombres STD {
  plantilla <clase T, clase Asignador = Asignador<T> > clase Vector;
// ...

Como todas las plantillas clase de C++, instancias de biblioteca estándar contenedores con diferente asignador argumentos son distintas tipos. Una función esperando un STD::Vector < int > argumento por lo tanto, sólo se aceptará una Vector crea una instancia con el asignador predeterminado.

Mejoras para distribuidores en 11 C ++

El último estándar C++ ha mejorado la interfaz asignador para permitir asignadores de "alcance", para que contenedores con asignaciones de memoria "nested", como vector de cadenas o un mapa de las listas de los conjuntos de tipos definidos por el usuario, pueden asegurar que toda la memoria es originario de asignador del contenedor. [21]

Ejemplo

__gnu_cxx::new_allocator < typename > clase de referencia de plantilla
https://gcc.GNU.org/onlinedocs/GCC-4.9.0/libstdc++/API/a00057.html
/ ** Procesador: 0 vendor_id: familia AuthenticAMD cpu: Modelo 16: 6 nombre del modelo: AMD Athlon (TM) II X 2 270 procesador escalonamiento: microcódigo 3: 0x10000c8 cpu MHz: 2000.000 caché Tamaño: 1024 KB... Procesador: 1 vendor_id: familia AuthenticAMD cpu: Modelo 16: 6 nombre del modelo: AMD Athlon (TM) II X 2 270 procesador escalonamiento: microcódigo 3: 0x10000c8 cpu MHz: 800.000 caché Tamaño: 1024 KB...
 Debian Linux debian 3.14-2-686-pae #1 SMP 3.14.15-2 (2014-08-09)... i686 GNU/Linux (Debian 4.9.1-12) gcc 4.9.1 Copyright (C) 2014 Free Software Foundation, Inc.
 Esto es software libre; vea la fuente para copiar las condiciones.  No hay ninguna garantía; ni para la comerciabilidad o aptitud para un propósito PARTICULAR.
... java@debian:~/java/eclipse$ ldd /usr/lib/i386-linux-gnu/libstdc++.so.6.0.20 linux-gate.so.1 (0xb7733000) libm.so.6 = > /lib/i386-linux-gnu/i686/cmov/libm.so.6 (0xb75da000) libc.so.6 = > /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb742f000) /lib/ld-linux.so.2 (0xb7734000) libgcc_s.so.1 = > /lib/i386-linux-gnu/libgcc_s.so.1 (0xb7411000) * /
#include < iostream >
usando espacio de nombres STD;
usando espacio de nombres __gnu_cxx;
 
clase RequiredAllocation
{
público:
RequiredAllocation ();
~ RequiredAllocation ();
STD::basic_string<Char> s = "¡ Hola mundo!\n";
};
RequiredAllocation::RequiredAllocation ()
{
	cout << "RequiredAllocation::RequiredAllocation()" << Endl;
}
RequiredAllocation::~ RequiredAllocation ()
{
	cout << "RequiredAllocation::~RequiredAllocation()" << Endl;
}
 
 
 
 
 
vacío Alloc(__gnu_cxx ::new_allocator<RequiredAllocation>* todos, unsigned int tamaño, vacío* PT, RequiredAllocation* t){
	probar
		{
todos->asignar (tamaño, pt);
			cout << todos->max_size () << Endl;
			para (Automático& e : t->s)
				{
					cout << e;
				}
		}
	captura (STD::bad_alloc& e)
		{
			cout << e.¿Qué () << Endl;
		}
}
 
int
principal ()
{
__gnu_cxx ::new_allocator<RequiredAllocation> *todos =
			Nuevo __gnu_cxx ::new_allocator<RequiredAllocation> ();
RequiredAllocation t;
	vacío* PT = &t;
 
	/ ** * ¿Qué no sucede cuando nueva puede encontrar ninguna tienda asignar? De forma predeterminada, el asignador lanza un stan - * excepción bad_alloc dard-biblioteca (para una alternativa, véase §11.2.4.1) * @C Bjarne Stroustrup The C++ Programming language * /
	unsigned int tamaño = 1073741824;
Alloc(todo, el tamaño, &PT, &t);
tamaño = 1;
Alloc(todo, el tamaño, &PT, &t);
 
	retorno 0;
}

Referencias

  1. ^ Stepanov, Alexander; Meng Lee (07 de marzo de 1994). "La biblioteca de plantillas estándar. Presentación ante el Comité de normas de C++" (PDF). Bibliotecas de Hewlett-Packard. 12 de mayo 2009.
  2. ^ a b c d e Stevens, Al (1995). "Al Stevens entrevistas Alex Stepanov". Journal del Dr. Dobb's. Programa archivado de la original en 01 de mayo de 2009. 12 de mayo 2009.
  3. ^ a b c Stroustrup, Bjarne (1997). El lenguaje de programación C++, 3ª edición. Addison-Wesley.
  4. ^ a b c d e f g Meyers, Scott (2001). STL efectiva: 50 específicas maneras de mejorar el uso de la biblioteca de plantillas estándar. Addison-Wesley.
  5. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): - lenguajes de programación C++ Requisitos de asignador de §20.1.5 [lib.allocator.requirements] párr. 4
  6. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): - lenguajes de programación C++ §20.4.1 el asignador predeterminado [lib.default.allocator]
  7. ^ Lo Russo, Graziano (1997). "Una entrevista con A. Stepanov". www.stlport.org. 13 de mayo 2009.
  8. ^ Halpern, Pablo (04 de febrero de 2008). "Swap asignador específicos y el comportamiento de movimiento" (PDF). ISO. 21 de agosto 2012.
  9. ^ Halpern, Pablo (22 de octubre de 2009). "Asignadores post eliminación de C++ conceptos (Rev. 1)" (PDF). ISO. 21 de agosto 2012.
  10. ^ Becker, Pete. "Cuestión LWG 1318: N2982 elimina las capacidades de asignador anterior (cerradas en marzo de 2011)". ISO. 21 de agosto 2012.
  11. ^ a b c ISO/IEC (2003). ISO/IEC 14882:2003(E): - lenguajes de programación C++ Requisitos de asignador de §20.1.5 [lib.allocator.requirements] párr. 2
  12. ^ a b ISO/IEC (2003). ISO/IEC 14882:2003(E): - lenguajes de programación C++ Requisitos de asignador de §20.1.5 [lib.allocator.requirements] párr. 5
  13. ^ Langer, Angelika; Klaus Kreft (1998). "Asignador tipos". Informe de C++. 13 de mayo 2009.
  14. ^ a b c Austern, Matthew (01 de diciembre de 2000). "El bibliotecario estándar: Qué bien asignadores de?". Journal del Dr. Dobb's. Programa archivado de la original en 28 de abril de 2009. 12 de mayo 2009.
  15. ^ a b c d Aue, Anthony (01 de septiembre de 2005). "Mejorar el rendimiento con piscina personalizada asignadores de STL". Journal del Dr. Dobb's. 13 de mayo 2009.
  16. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): - lenguajes de programación C++ miembros §20.4.1.1 asignador [lib.allocator.members] párr. 3
  17. ^ a b c Alexandrescu, Andrei (2001). Moderno diseño de C++. Addison-Wesley. p. 352. ISBN0-201-70431-5.
  18. ^ Vlasceanu, Christian (01 de abril de 2001). "Depuración de errores de memoria con asignadores personalizados". Journal del Dr. Dobb's. 14 de mayo 2009.
  19. ^ a b Austern, Matthew (01 de diciembre de 2001). "El bibliotecario estándar: un asignador de depuración". Journal del Dr. Dobb's. 14 de mayo 2009.
  20. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): - lenguajes de programación C++ Secuencias de §23.2 [lib.sequences] párr. 1
  21. ^ Halpern, Pablo (29 de febrero de 2008). "El ámbito asignador modelo (Rev 2)" (PDF). ISO. 21 de agosto 2012.


Enlaces externos

  • CodeGuru: Distribuidores (STL).
  • Un artículo introductorio "Asignador estándar C++, una introducción y aplicación".
  • Una implementación de asignador personalizado basada en malloc

Otras Páginas

Obtenido de"http://en.copro.org/w/index.php?title=Allocator _ (C % 2B % 2B) & oldid = 656926708"