2010-06-23 6 views
31

En C las funciones estándar de manejo de 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 un problema con, por ejemplo, esta función:¿Por qué no hay una funcionalidad de reasignación en asignadores de C++?

bool reallocate (pointer ptr, size_type num_now, size_type num_requested); 

donde

  • ptr está asignado previamente con el mismo asignador de num_now objetos;
  • num_requested> = num_now;

y la semántica de la siguiente manera:

  • si asignador puede expandir dado bloque de memoria en ptr de tamaño para num_now objetos a num_requested objetos, lo hace (salida memoria adicional no inicializado) y devuelve true;
  • else no hace nada y devuelve false.

De acuerdo, esto no es muy simple, pero los asignadores, como tengo entendido, están destinados principalmente para contenedores y el código de los contenedores ya es complicado.

dado una función de este tipo, 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 

asignadores que son incapaces de cambiar el tamaño de la memoria por completo simplemente podría implementar una función de este tipo por return false; incondicional.

¿Hay tan pocas implementaciones de asignadores con capacidad de reasignación que no merezca la pena molestarse? ¿O hay algunos problemas que pasé por alto?

+13

+1, esta es una pregunta que siempre me molestó. –

+0

La opinión de Stroustrup sobre esto: http://www2.research.att.com/~bs/bs_faq2.html#renew; delega el problema en el funcionamiento interno del vector, pero no dice por qué no hay un mecanismo como "renovar" para simplificar el crecimiento de la matriz. –

+1

No hay nada que detenga 'std :: vector' en algunos casos (por ejemplo, sabe que usa el asignador estándar). La biblioteca estándar puede usar el conocimiento del sistema subyacente. – KeithB

Respuesta

17

Desde: http://www.sgi.com/tech/stl/alloc.html

Este es probablemente el más cuestionable decisión diseño. Tendría probablemente fue un poco más útil que proporcionar una versión de reasignación que cambió el tamaño del objeto existente sin copiar o devolvió NULL. Esto lo habría hecho directamente útil para objetos con copia constructores. También habría evitado copias innecesarias en los casos en los que el objeto original no había sido completamente llenados.

Desafortunadamente, esto habría uso prohibido de realloc de la biblioteca C . Esto, a su vez, habría agregado la complejidad a muchas implementaciones del asignador , y habría dificultado la interacción con las herramientas de depuración de memoria . Por lo tanto, decidimos en contra de esta alternativa.

+2

Creo que es una pena que al menos no hayan agregado un método de reasignación a la interfaz de Allocator. Podría haber sido implementado para simplemente liberar un bloque existente y asignar uno nuevo, pero habría dado la posibilidad de implementar uno nuevo más tarde sin rehacer todo el código usando la interfaz de Allocator. – stinky472

1

Supongo que esta es una de las cosas en las que Dios salió mal, pero yo era demasiado flojo para escribir en el comité de normas.

Debería haber habido un realloc para las asignaciones de matriz:

p = renew(p) [128];

o algo por el estilo.

+0

Si usa vectores en lugar de matrices, hay '.reserve()'. ¿Por qué agregar una nueva funcionalidad para matrices cuando los vectores son generalmente mejores? –

+1

@DavidThornley, los vectores bajo el capó deben pasar por la interfaz del asignador. Por lo tanto, parece que un vector no puede liberar la memoria no utilizada de la manera más eficiente posible. (¡Pero creo/espero que me esté perdiendo algo aquí!) –

+1

@DavidThornley - exactamente lo que dijo Aaron. _Todo el mundo_ dice "use' vector' "- ¡pero están hablando en el nivel incorrecto de abstracción! 'vector' en sí tiene que construirse sobre (y de hecho está construido en) las rutinas de asignación de nivel inferior que le permiten obtener memoria no inicializada, y así sucesivamente. Si esas rutinas no ofrecen una función de "reasignación" de bajo nivel, ciertamente 'vector' no puede tampoco. Por supuesto, puede ofrecer 'resize' y' reserver' y todo lo demás, pero bajo las coberturas solo asignan nuevos bloques y copian las cosas. No hay nada como expandir un bloque existente. – BeeOnRope

3

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 a la administración de la memoria de dirección que en C. Acepto que hay casos que un realloc () sería útil, pero la presión para remediar esto es mínima, ya que casi toda la funcionalidad resultante se puede obtener mediante el uso de contenedores en su lugar.

+1

No estoy seguro de estar de acuerdo en que lo pensaran menos debido a OOP. La ubicación nueva es un ejemplo de una característica específicamente para la administración de memoria de los objetos. –

+0

No digo que lo pensaran menos, solo que la sintaxis real se diseñó para poner más énfasis en OOP que en la administración directa de memoria en los casos en que las dos son opciones diferentes. La colocación nueva es un ejemplo perfecto de dónde el programador usa los dos juntos en lugar de uno que reemplaza al otro. – tlayton

+0

Actualmente, la ubicación nueva es el aparato que * permite * administración de memoria personalizada, por lo demás muy dura (específicamente agrupaciones de memoria) dentro de las instalaciones de C++. Lo considero un dispositivo que * fomenta * la personalización de la gestión de memoria con bibliotecas de códigos C++. –

8

Lo que está 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.

+0

Un buen uso de esta funcionalidad para esto sería contenedores dispersos. Usar vectores, especialmente con la memoria preasignada, en ellos vencería por completo su propósito (la dispersión está destinada a conservar la memoria). – doublep

+1

@doublep: si desea contenedores dispersos, ni arreglos (dinámicamente) asignados ni vectores son lo que desea. –

+0

@Martin York: Bueno, por ejemplo, la biblioteca Google Sparsehash utiliza matrices asignadas dinámicamente y logra muy buenos resultados. – doublep

11

Esto es en realidad un defecto de diseño que Alexandrescu señala con los asignadores estándar (no operador new []/delete [] pero lo que eran originalmente los asignadores stl 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: aborda 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.

+1

+1 Un enlace para copia de seguridad: http://www.stepanovpapers.com/notes.pdf –

Cuestiones relacionadas