Il est en fait nécessaire d'implémenter tout type de structure de données qui alloue plus de mémoire que le minimum requis pour le nombre d'éléments insérés (c'est-à-dire autre chose qu'une structure liée qui alloue un nœud à la fois).
Prenez des conteneurs aiment unordered_map
, vector
ou deque
. Tous ces éléments allouent plus de mémoire que ce qui est minimalement requis pour les éléments que vous avez insérés jusqu'à présent pour éviter d'exiger une allocation de segment de mémoire pour chaque insertion unique. Prenons l' vector
exemple le plus simple.
Quand vous faites:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... qui ne construit pas réellement mille Foos. Il leur alloue / réserve simplement de la mémoire. Si vous vector
n'utilisiez pas le placement nouveau ici, ce serait une construction par défaut Foos
partout et vous devrez invoquer leurs destructeurs même pour les éléments que vous n'avez même jamais insérés en premier lieu.
Allocation! = Construction, Libération! = Destruction
D'une manière générale, pour implémenter de nombreuses structures de données comme ci-dessus, vous ne pouvez pas traiter l'allocation de mémoire et la construction d'éléments comme une chose indivisible, et vous ne pouvez pas non plus traiter la libération de mémoire et la destruction d'éléments comme une chose indivisible.
Il doit y avoir une séparation entre ces idées pour éviter d'invoquer inutilement des constructeurs et des destructeurs à gauche et à droite, et c'est pourquoi la bibliothèque standard sépare l'idée de std::allocator
(qui ne construit pas ou ne détruit pas les éléments lorsqu'elle alloue / libère de la mémoire *) loin de les conteneurs qui l'utilisent qui construisent manuellement des éléments en utilisant placement new et détruisent manuellement des éléments en utilisant des invocations explicites de destructeurs.
- Je déteste le design de
std::allocator
mais c'est un sujet différent sur lequel j'éviterai de parler. :-RÉ
Donc, de toute façon, j'ai tendance à l'utiliser beaucoup car j'ai écrit un certain nombre de conteneurs C ++ standard à usage général qui ne pouvaient pas être construits en fonction des conteneurs existants. Parmi eux, une petite implémentation vectorielle que j'ai construite il y a quelques décennies pour éviter les allocations de tas dans les cas courants, et un tri efficace en mémoire (n'alloue pas un nœud à la fois). Dans les deux cas, je ne pouvais pas vraiment les implémenter en utilisant les conteneurs existants, et j'ai donc dû placement new
éviter d'utiliser des constructeurs et des destructeurs superflus sur des choses inutiles à gauche et à droite.
Naturellement, si vous travaillez avec des allocateurs personnalisés pour allouer des objets individuellement, comme une liste gratuite, vous voudrez également généralement utiliser placement new
, comme ceci (exemple de base qui ne dérange pas avec la sécurité d'exception ou RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);