¿Por qué no hay una funcionalidad de reasignación en los asignadores de C++?
malloc (5)
De: http://www.sgi.com/tech/stl/alloc.html
Esta es probablemente la decisión de diseño más cuestionable. Probablemente hubiera sido un poco más útil proporcionar una versión de reasignación que cambiara el tamaño del objeto existente sin copiar o devolviera NULL. Esto lo habría hecho directamente útil para objetos con constructores de copia. También habría evitado la copia innecesaria en los casos en que el objeto original no se había completado por completo.
Desafortunadamente, esto habría prohibido el uso de realloc de la biblioteca C. Esto, a su vez, habría agregado complejidad a muchas implementaciones de asignadores, y habría dificultado la interacción con las herramientas de depuración de memoria. Por lo tanto, decidimos en contra de esta alternativa.
En C, las funciones estándar de manejo de la memoria son malloc()
, realloc()
y free()
. Sin embargo, las asignaciones stdlib de C ++ solo son paralelas a dos de ellas: no hay una función de reasignación. Por supuesto, no sería posible hacer exactamente lo mismo que realloc()
, porque simplemente copiar la memoria no es apropiado para tipos no agregados. Pero, ¿habría algún problema con, digamos, esta función?
bool reallocate (pointer ptr, size_type num_now, size_type num_requested);
dónde
-
ptr
está previamente asignado con el mismo asignador para objetosnum_now
; -
num_requested
> =num_now
;
y semántica de la siguiente manera:
- si el asignador puede expandir un bloque de memoria dado en
ptr
desde el tamaño paranum_now
objetos anum_requested
objetos, lo hace (dejando memoria adicional sin inicializar) y devuelvetrue
; - de lo contrario, no hace nada y devuelve
false
.
Por supuesto, esto no es muy simple, pero los asignadores, en mi opinión, están destinados principalmente a contenedores y el código de los contenedores ya es complicado.
Dada dicha función, std::vector
, por ejemplo, podría crecer de la siguiente manera (pseudocódigo):
if (allocator.reallocate (buffer, capacity, new_capacity))
capacity = new_capacity; // That''s all we need to do
else
... // Do the standard reallocation by using a different buffer,
// copying data and freeing the current one
Los asignadores que son incapaces de cambiar por completo el tamaño de la memoria podrían simplemente implementar dicha función mediante un return false;
incondicional return false;
.
¿Hay tan poca implementación de asignador capaz de reasignar que no valdría la pena molestarse? ¿O hay algunos problemas que pasé por alto?
Debido a la naturaleza orientada a objetos de C ++, y la inclusión de varios tipos de contenedores estándar, creo que es simplemente que se prestó menos atención al manejo de la memoria de dirección que en C. Acepto que hay casos en los que sería útil un realloc () , pero la presión para remediar esto es mínima, ya que casi la totalidad de la funcionalidad resultante se puede obtener mediante el uso de contenedores en su lugar.
Esto es en realidad un error de diseño que Alexandrescu señala con los asignadores estándar (no con el operador new [] / delete [] pero que originalmente eran los stl allocators utilizados para implementar std :: vector, por ejemplo).
Un realloc puede ocurrir significativamente más rápido que un malloc, memcpy y libre. Sin embargo, aunque el bloque de memoria real se puede cambiar de tamaño, también puede mover la memoria a una nueva ubicación. En el último caso, si el bloque de memoria consiste en no POD, todos los objetos deberán destruirse y copiarse después del realloc.
Lo principal que la biblioteca estándar necesita para acomodar esto como una posibilidad es una función de reasignación como parte de la interfaz pública del asignador estándar. Una clase como std :: vector ciertamente podría usarlo incluso si la implementación predeterminada es malloc el bloque de nuevo tamaño y liberar el antiguo. Sin embargo, necesitaría ser una función que sea capaz de destruir y copiar y construir los objetos en la memoria, no puede tratar la memoria de una manera opaca si lo hiciera. Existe una pequeña complejidad involucrada allí y requeriría un poco más de trabajo de plantilla, que puede ser el motivo por el que se omitió en la biblioteca estándar.
std :: vector <...> :: reserve no es suficiente: trata un caso diferente donde se puede anticipar el tamaño del contenedor. Para listas de tamaño verdaderamente variable, una solución realloc podría hacer que los contenedores contiguos como std :: vector sean mucho más rápidos, especialmente si puede tratar casos de realloc donde el bloque de memoria se redimensionó correctamente sin ser movido, en cuyo caso puede omitir la copia de llamada constructores y destructores para los objetos en la memoria.
Lo que estás pidiendo es esencialmente lo que vector::reserve
hace. Sin semántica de movimiento para los objetos, no hay forma de reasignar la memoria y mover los objetos sin copiar y destruir.
Supongo que esta es una de las cosas en las que Dios salió mal, pero yo era demasiado flojo para escribirle al comité de normas.
Debería haber un realloc para las asignaciones de matriz:
p = renew(p) [128];
o algo así.