Red de conocimiento informático - Problemas con los teléfonos móviles - Comentarios del código fuente Stl

Comentarios del código fuente Stl

Hablemos del asignador en C. Sabemos que hay muchos contenedores definidos en C STL y el segundo parámetro de plantilla de cada contenedor es un asignador de tipos. Por ejemplo, en VC10, la plantilla para la clase de vector se declara como:

Template ltclass _Ty, class _Ax = allocator lt_ Ty gt gt

Vector de clasificación

Sin embargo, básicamente pocas personas personalizan un dispensador. Primero, el asignador predeterminado es suficiente; segundo, realmente no sé cómo usarlo. En términos generales, no necesitamos redefinir un asignador. El método de personalización es principalmente para mejorar el rendimiento de las operaciones relacionadas con la asignación de memoria. El rendimiento de STL es bastante bueno. De hecho, en la plataforma Windows, la implementación subyacente de new se basa en la función malloc del lenguaje C; la familia de funciones Malloc se implementa en base a Windows HeapCreate, HeapAlloc, HeapFree y otras API relacionadas. (Para obtener más detalles, consulte heapinit.c, malloc.c, new.cpp y otras funciones relacionadas en el directorio VSInstallFolder\VC\CRT\src\CRT\src).

Dejando a un lado los problemas de rendimiento, veamos cómo implementar nuestro propio asignador.

En el documento estándar C 2003, no hay mucha descripción del asignador. Probablemente hay dos ubicaciones principales: 20.1.5 Requisitos del asignador y 20.4.1 Asignador predeterminado. Aunque el contenido no es mucho, nos basta con escribir nuestro propio asignador.

De acuerdo con los requisitos del asignador, debemos proporcionar algunas definiciones de tipo:

1: Plantilla lttypename T gt

2: Clase CHxAllocator

3: {

4: public:

5: // typedefs...

6: typedef T value_type;

7: puntero typedef value_type*;

8: referencia de amplificador de tipo de valor typedef;

9: typedef value_type constante*puntero_constante;

valor typedef _ type const ampconst _ referencia

11: typedef tamaño _ t tamaño _ tipo

12: typedef ptrdiff _ t diferencia _ tipo

13:

14: // rebind...

15: Plantilla lttypename_other gt estructura rebind { typedef CHxAllocator lt_other gt other }; p>Hay una cosa que no es fácil de entender aquí: volver a vincular. La descripción de la revinculación en el estándar C es la siguiente:

La plantilla de clase miembro rebind en la tabla anterior es en realidad una plantilla typedef: si el asignador de nombres está vinculado a SomeAllocator ltT gt, entonces

Allocator::Rebind ltU gt* other es del mismo tipo que SomeAllocator ltU gt.

¿Qué quieres decir? Se puede utilizar un ejemplo sencillo para ilustrar:

Aprendí estructuras de datos en la escuela, como pilas, listas enlazadas unidireccionales y árboles. Comparemos pilas y listas para ver cuáles son las grandes diferencias. Dejando de lado las diferencias en las estructuras de datos, desde la perspectiva del asignador, podemos encontrar que la pila almacena los elementos en sí, mientras que la lista vinculada en realidad no almacena los elementos en sí directamente.

Para mantener una lista, necesitamos al menos un puntero llamado next. Por lo tanto, aunque es una lista de entradas almacenadas, list

A continuación, debemos proporcionar otras interfaces. Según la descripción del asignador predeterminado, debemos proporcionar la siguiente interfaz:

Dirección de puntero (valor de referencia) constante

Dirección de puntero_constante (valor de referencia_constante)

Devuelve la dirección de val.

La asignación de puntero (size_type cnt, CHxAllocator ltvoid gt* const _ pointer pHint = 0) asigna espacio. Similar a malloc. Se puede ignorar PHint, principalmente para mejorar el rendimiento de la biblioteca de clases.

Void deallocate(pointer p, size_type n) libera espacio, similar a free.

El número máximo que se puede asignar.

Voidconstruct(pointer p, const_referenceval) llena el espacio señalado por la dirección p con val. Debe utilizar la ubicación nueva para asegurarse de que se llame al constructor.

La destrucción vacía (puntero p) destruye el contenido del bloque de memoria señalado por p, generalmente realizado mostrando el destructor.

Allocator() throws()

Allocator(const_reference) throws()

Plantilla lttypename_other gt allocator(CHxAllocator lt_other gtconst amp) throw()

~CHxAllocator() throw()

Varios constructores y destructores

Cómo implementar estas funciones, simplemente copie Simplemente impleméntelo en la biblioteca estándar.

Si desea utilizar malloc en lugar de C, también puede escribir:

1: asignación de puntero (size_type cnt, CHxAllocator ltvoid gt* const _ pointer pHint = 0)

2: {

3: sin referencia _ PARAMETER(pHint);

4:

5: if (CNT lt; = 0)

6: {

7: Devuelve 0

8: }

9:

10: void * pMem = nullptr;

11: if(max _ size() lt; CNT | |(pMem = malloc(CNT * sizeof(value _ type))) = = NULL)

12: {

13: lanzar STD::bad_alloc(0);

14: }

15:

16: devolver static_ cast lt puntero gt(pMem);

17: }

18:

19: void repartocate(puntero p, tamaño_tipo)

20 : {

21: Gratis (p);

22: }

23:

24: constructor vacío (puntero p, valor de referencia_const)

25: {

26:::new((void *)p)T(val);

27: }

28:

29: Destrucción no válida (puntero p)

30: {

31: p->;~ T() ;

32: }

Básicamente, acabamos de implementar nuestro propio asignador. Además, además de estas funciones principales de la interfaz, también es necesario implementar los operadores de comparación == y ! =, pero estas letras devuelven directamente verdadero o falso según la documentación estándar.

Como dije al principio, el objetivo principal de reescribir el asignador es mejorar el rendimiento. Entonces, ¿cómo se puede mejorar el rendimiento? ¿Utilizar directamente la API de memoria dinámica HeapXXXX de Windows? De hecho, cuando lo use usted mismo, encontrará que la mejora del rendimiento no es obvia. Porque pasar new, luego malloc y finalmente heapaloc no es más que llamar a heapaloc directamente. Cómo implementar un asignador de alto rendimiento requiere la idea de un grupo de memoria. Además, el análisis del código fuente stl de Hou Jie utilizó una idea similar para analizar la asignación implementada por SGI STL.