En realidad, es necesario implementar cualquier tipo de estructura de datos que asigne más memoria que la mínimamente requerida para el número de elementos insertados (es decir, cualquier otra cosa que no sea una estructura vinculada que asigna un nodo a la vez).
Contenedores para llevar gusta unordered_map
, vector
o deque
. Todos estos asignan más memoria de la mínima requerida para los elementos que ha insertado hasta ahora para evitar requerir una asignación de montón para cada inserción. Usemos vector
como el ejemplo más simple.
Cuando tu lo hagas:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... eso en realidad no construye mil Foos. Simplemente asigna / reserva memoria para ellos. Si vector
no usó la ubicación nueva aquí, sería una construcción predeterminada en Foos
todo el lugar, así como tener que invocar sus destructores incluso para elementos que nunca insertó en primer lugar.
Asignación! = Construcción, Liberación! = Destrucción
En términos generales, para implementar muchas estructuras de datos como las anteriores, no puede tratar la asignación de memoria y la construcción de elementos como una cosa indivisible, y tampoco puede tratar la liberación de memoria y la destrucción de elementos como una cosa indivisible.
Tiene que haber una separación entre estas ideas para evitar invocar innecesariamente constructores y destructores innecesariamente a izquierda y derecha, y es por eso que la biblioteca estándar separa la idea de std::allocator
(que no construye ni destruye elementos cuando asigna / libera memoria *) los contenedores que lo usan que construyen elementos manualmente mediante la colocación de elementos nuevos y destruyen elementos manualmente mediante invocaciones explícitas de destructores.
- Odio el diseño de,
std::allocator
pero ese es un tema diferente sobre el que evitaré despotricar. :-RE
De todos modos, tiendo a usarlo mucho ya que he escrito una serie de contenedores C ++ de propósito general que cumplen con los estándares que no se pudieron construir en términos de los existentes. Entre ellos se incluye una pequeña implementación de vectores que construí hace un par de décadas para evitar las asignaciones de almacenamiento dinámico en casos comunes, y un trie de memoria eficiente (no asigna un nodo a la vez). En ambos casos, realmente no pude implementarlos usando los contenedores existentes, por lo que tuve que usar placement new
para evitar invocar superfluos constructores y destructores en cosas innecesarias a izquierda y derecha.
Naturalmente, si alguna vez trabaja con asignadores personalizados para asignar objetos individualmente, como una lista gratuita, entonces generalmente también querrá usar placement new
, como este (ejemplo básico que no molesta con la seguridad de excepción o RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);