W rzeczywistości jest to rodzaj implementacji jakiejkolwiek struktury danych, która przydziela więcej pamięci niż jest minimalnie wymagana dla liczby wstawianych elementów (tj. Wszystko inne niż struktura połączona, która przydziela jeden węzeł na raz).
Przystawki pojemniki podoba unordered_map
, vector
albo deque
. Wszystkie one alokują więcej pamięci, niż jest to wymagane minimalnie dla elementów, które dotychczas wstawiłeś, aby uniknąć konieczności alokacji sterty dla każdego pojedynczego wstawienia. Użyjmy vector
jako najprostszego przykładu.
Kiedy to zrobisz:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... to nie konstruuje tysiąc Foos. Po prostu alokuje / rezerwuje dla nich pamięć. Gdyby vector
nie używał nowego umieszczania tutaj, byłoby to domyślne budowanie w Foos
całym miejscu, a także zmuszanie do wywoływania ich destrukterów nawet dla elementów, których nigdy nawet nie wstawiłeś.
Alokacja! = Budowa, uwolnienie! = Zniszczenie
Mówiąc ogólnie, aby zaimplementować wiele struktur danych takich jak powyżej, nie można traktować przydzielania pamięci i konstruowania elementów jako jednej niepodzielnej rzeczy, podobnie jak nie można traktować zwalniania pamięci i niszczenia elementów jako jednej niepodzielnej rzeczy.
Pomiędzy tymi pomysłami musi istnieć rozdział, aby uniknąć niepotrzebnego wywoływania konstruktorów i destruktorów niepotrzebnie w lewo i w prawo, i dlatego standardowa biblioteka oddziela ideę std::allocator
(która nie konstruuje ani nie niszczy elementów, gdy alokuje / zwalnia pamięć *) z dala od kontenery, które go używają, które ręcznie konstruują elementy za pomocą umieszczania nowych i ręcznie niszczą elementy za pomocą jawnych wywołań destruktorów.
- Nienawidzę projektu
std::allocator
ale to inny temat, o którym będę się nie drażnić. :-RE
Tak czy inaczej, często go używam, ponieważ napisałem wiele ogólnie dostępnych kontenerów C ++ zgodnych ze standardami, których nie można zbudować w stosunku do istniejących. Wśród nich jest mała implementacja wektorowa, którą zbudowałem kilkadziesiąt lat temu, aby uniknąć alokacji sterty w typowych przypadkach, oraz trie wydajne pod względem pamięci (nie alokuje jednego węzła na raz). W obu przypadkach nie mogłem tak naprawdę zaimplementować ich przy użyciu istniejących kontenerów, więc musiałem użyć, placement new
aby uniknąć zbędnego wywoływania konstruktorów i destruktorów na rzeczach niepotrzebnych z lewej i prawej strony.
Oczywiście, jeśli kiedykolwiek pracujesz z niestandardowymi alokatorami, aby alokować obiekty indywidualnie, jak na przykład bezpłatna lista, to na ogół powinieneś również użyć placement new
takiego, jak ten (podstawowy przykład, który nie przeszkadza w bezpieczeństwie wyjątków lub RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);