Es ist tatsächlich erforderlich, jede Art von Datenstruktur zu implementieren, die mehr Speicher zuweist, als für die Anzahl der eingefügten Elemente minimal erforderlich ist (dh alles andere als eine verknüpfte Struktur, die jeweils einen Knoten zuweist).
Nehmen Sie Container mögen unordered_map
, vector
oder deque
. Diese weisen alle mehr Speicher zu, als für die bisher eingefügten Elemente minimal erforderlich ist, um zu vermeiden, dass für jede einzelne Einfügung eine Heap-Zuordnung erforderlich ist. Verwenden wir vector
als einfachstes Beispiel.
Wenn Sie das tun:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... das baut eigentlich nicht tausend Foos. Es reserviert / reserviert einfach Speicher für sie. Wenn vector
hier keine neue Platzierung verwendet würde, wäre dies eine StandardkonstruktionFoos
überall und die Notwendigkeit, deren Destruktoren auch für Elemente aufzurufen, die Sie noch nie zuvor eingefügt haben.
Zuteilung! = Bau, Befreiung! = Zerstörung
Um viele Datenstrukturen wie die oben genannten zu implementieren, können Sie das Zuweisen von Speicher und das Erstellen von Elementen im Allgemeinen nicht als eine unteilbare Sache behandeln, und Sie können das Freigeben von Speicher und das Zerstören von Elementen ebenfalls nicht als eine unteilbare Sache behandeln.
Es muss eine Trennung zwischen diesen Ideen geben, um zu vermeiden, dass Konstruktoren und Destruktoren unnötig links und rechts aufgerufen werden. Deshalb trennt die Standardbibliothek die Idee von std::allocator
(die keine Elemente konstruiert oder zerstört, wenn sie Speicher zuweist / freigibt *) von Die Container, die es verwenden, erstellen Elemente manuell mithilfe der Platzierung neu und zerstören Elemente manuell mithilfe expliziter Aufrufe von Destruktoren.
- Ich hasse das Design von,
std::allocator
aber das ist ein anderes Thema, über das ich nicht schimpfen werde. :-D
Daher neige ich dazu, es häufig zu verwenden, da ich eine Reihe von universellen, standardkonformen C ++ - Containern geschrieben habe, die in Bezug auf die vorhandenen nicht erstellt werden konnten. Darunter befindet sich eine kleine Vektorimplementierung, die ich vor einigen Jahrzehnten erstellt habe, um Heap-Zuweisungen in häufigen Fällen zu vermeiden, und ein speichereffizienter Versuch (weist nicht jeweils einen Knoten zu). In beiden Fällen konnte ich sie mit den vorhandenen Containern nicht wirklich implementieren und musste daher placement new
vermeiden, Konstruktoren und Destruktoren überflüssig auf unnötige Links und Rechts aufzurufen.
Wenn Sie jemals mit benutzerdefinierten Zuweisern arbeiten, um Objekte wie eine kostenlose Liste einzeln zuzuweisen, möchten Sie placement new
diese natürlich auch im Allgemeinen wie folgt verwenden (grundlegendes Beispiel, das sich nicht mit Ausnahmesicherheit oder RAII befasst):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);