Czy ktoś tutaj kiedykolwiek używał „nowego miejsca docelowego” w C ++? Jeśli tak, to po co? Wydaje mi się, że przydałby się tylko na sprzęcie odwzorowanym w pamięci.
c++
memory-management
new-operator
placement-new
Head Geek
źródło
źródło
p = pt
użyć operatora przypisaniaPoint
zamiast tegonew(&p) Point(pt)
? Zastanawiam się nad różnicami między nimi. Czy pierwszy zadzwoniłbyoperator=
do Pointa, a drugi do kopii konstruktoraPoint
? ale wciąż nie jestem do końca pewien, dlaczego jedno jest lepsze od drugiego.U::operator=
został właśnie wywołany.Odpowiedzi:
Umieszczenie nowego pozwala skonstruować obiekt w pamięci, który jest już przydzielony.
Możesz to zrobić w celu optymalizacji, gdy musisz zbudować wiele instancji obiektu, a szybsze jest nie przydzielanie pamięci za każdym razem, gdy potrzebujesz nowej instancji. Zamiast tego bardziej wydajne może być wykonanie pojedynczej alokacji dla części pamięci, która może pomieścić wiele obiektów, nawet jeśli nie chcesz korzystać z nich jednocześnie.
DevX daje dobry przykład :
Możesz także mieć pewność, że nie wystąpi błąd alokacji w określonej części kodu krytycznego (na przykład w kodzie wykonywanym przez rozrusznik serca). W takim przypadku należy wcześniej przydzielić pamięć, a następnie użyć umieszczenia nowego w sekcji krytycznej.
Dezalokacja w miejscu docelowym nowość
Nie należy cofać przydziału każdego obiektu używającego bufora pamięci. Zamiast tego powinieneś usunąć [] tylko oryginalny bufor. Będziesz musiał ręcznie wywołać destruktory swoich klas. Dobra sugestia na ten temat znajduje się w FAQ Stroustrup na temat: Czy istnieje „usunięcie miejsca docelowego” ?
źródło
delete[]
pierwotnegochar
bufora jest niezdefiniowane . Korzystanie z umieszczanianew
zakończyło żywotność oryginalnychchar
obiektów poprzez ponowne użycie ich przechowywania. Jeśli teraz wywołaszdelete[] buf
dynamiczny typ wskazanego obiektu (ów), nie będzie on już odpowiadał ich typowi statycznemu, więc zachowałeś niezdefiniowane zachowanie. Bardziej spójne jest użycieoperator new
/operator delete
przydzielenie surowej pamięci przeznaczonej do użycia przez umieszczenienew
.#include <new>
.Używamy go z niestandardowymi pulami pamięci. Tylko szkic:
Teraz możesz grupować obiekty razem w jednej arenie pamięci, wybierać alokator, który jest bardzo szybki, ale nie dokonuje dealokacji, używa mapowania pamięci i innych semantycznych, które chcesz narzucić, wybierając pulę i przekazując ją jako argument do umiejscowienia obiektu nowy operator.
źródło
allocate()
gdzieś przekazać wyrównania ?Jest to przydatne, jeśli chcesz oddzielić alokację od inicjalizacji. STL używa nowego umieszczania do tworzenia elementów kontenera.
źródło
Użyłem go w programowaniu w czasie rzeczywistym. Zazwyczaj nie chcemy przeprowadzać żadnego dynamicznego przydzielania (lub zwalniania) po uruchomieniu systemu, ponieważ nie ma gwarancji, jak długo to zajmie.
To, co mogę zrobić, to wstępnie przydzielić dużą część pamięci (wystarczająco dużą, aby pomieścić dowolną ilość tego, czego może wymagać klasa). Następnie, kiedy wymyślę w czasie wykonywania, jak konstruować te rzeczy, nowe umieszczanie może być użyte do konstruowania obiektów dokładnie tam, gdzie chcę. Wiem, że użyłem go w jednej sytuacji, aby pomóc stworzyć heterogeniczny okrągły bufor .
Z pewnością nie jest to dla osób o słabym sercu, ale właśnie dlatego sprawiają, że składnia jest dla tego dość osobliwa.
źródło
Użyłem go do konstruowania obiektów przydzielonych na stosie za pomocą metody przydzielania ().
bezwstydna wtyczka: blogowałem o tym tutaj .
źródło
boost::array
. Czy możesz to trochę rozwinąć?Head Geek: BINGO! Masz to całkowicie - właśnie do tego jest idealny. W wielu środowiskach osadzonych ograniczenia zewnętrzne i / lub ogólny scenariusz użycia zmusza programistę do oddzielenia alokacji obiektu od jego inicjalizacji. Zgrupowane razem, C ++ nazywa to „wystąpieniem”; ale ilekroć działanie konstruktora musi być jawnie wywoływane BEZ alokacji dynamicznej lub automatycznej, sposobem na to jest umieszczenie nowego. Jest to również doskonały sposób na zlokalizowanie globalnego obiektu C ++, który jest przypięty do adresu komponentu sprzętowego (we / wy mapowane w pamięci) lub dowolnego obiektu statycznego, który z jakiegokolwiek powodu musi znajdować się pod stałym adresem.
źródło
Użyłem go do stworzenia klasy Variant (tj. Obiektu, który może reprezentować pojedynczą wartość, która może być jedną z wielu różnych typów).
Jeśli wszystkie typy wartości obsługiwane przez klasę Variant są typami POD (np. Int, float, double, bool), wówczas wystarczające jest połączenie oznaczone w stylu C, ale jeśli chcesz, aby niektóre typy wartości były obiektami C ++ ( np. std :: string), funkcja unii C nie zadziała, ponieważ typy danych inne niż POD mogą nie zostać zadeklarowane jako część unii.
Zamiast tego przydzielam tablicę bajtów, która jest wystarczająco duża (np. Sizeof (the_largest_data_type_I_support)) i używam umieszczania new, aby zainicjować odpowiedni obiekt C ++ w tym obszarze, gdy Variant jest ustawiony na przechowywanie wartości tego typu. (I miejsce docelowe usuwa się wcześniej przy zmianie innego typu danych niż POD), oczywiście)
źródło
new
do zainicjowania swojej podklasy innej niż POD. Ref: stackoverflow.com/a/33289972/2757035 Ponowne wynalezienie tego koła za pomocą arbitralnie dużej tablicy bajtów jest imponującą akrobacją, ale wydaje się całkowicie niepotrzebne, więc co przegapiłem? :)Nowe umieszczanie jest również bardzo przydatne podczas szeregowania (powiedzmy przy pomocy boost :: seriization). W ciągu 10 lat c ++ to dopiero drugi przypadek, dla którego potrzebowałem nowego miejsca (trzeci, jeśli uwzględnisz wywiady :)).
źródło
Jest to również przydatne, gdy chcesz ponownie zainicjować struktury globalne lub statycznie przydzielone.
Stary sposób
memset()
języka C służył do ustawiania wszystkich elementów na 0. Nie można tego zrobić w C ++ z powodu vtables i niestandardowych konstruktorów obiektów.Czasami używam następujących
źródło
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
albodeque
. 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żyjmyvector
jako najprostszego przykładu.Kiedy to zrobisz:
... 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 wFoos
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.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):źródło
Przydaje się, jeśli budujesz jądro - gdzie umieścisz kod jądra, który czytasz z dysku lub z pagetable? Musisz wiedzieć, gdzie skoczyć.
Lub w innych, bardzo rzadkich okolicznościach, na przykład gdy masz dużo przydzielonego pokoju i chcesz umieścić kilka struktur za sobą. Można je w ten sposób spakować bez potrzeby użycia operatora offsetof (). Istnieją jednak inne sztuczki.
Wierzę również, że niektóre implementacje STL wykorzystują nowe umieszczanie, takie jak std :: vector. W ten sposób przydzielają miejsce na 2 elementy i nie muszą zawsze zmieniać położenia.
źródło
Myślę, że nie została to podkreślona żadna odpowiedź, ale kolejny dobry przykład i zastosowanie nowego miejsca docelowego jest zmniejszenie fragmentacji pamięci (poprzez użycie pul pamięci). Jest to szczególnie przydatne w systemach wbudowanych io wysokiej dostępności. W tym ostatnim przypadku jest to szczególnie ważne, ponieważ dla systemu, który musi działać 24/365 dni, bardzo ważne jest, aby nie było fragmentacji. Ten problem nie ma nic wspólnego z wyciekiem pamięci.
Nawet jeśli używana jest bardzo dobra implementacja malloc (lub podobna funkcja zarządzania pamięcią), bardzo trudno jest poradzić sobie z fragmentacją przez długi czas. W pewnym momencie, jeśli nie będziesz sprytnie zarządzać wywołaniami rezerwacji / zwalniania pamięci, możesz skończyć z wieloma małymi lukami, które są trudne do ponownego wykorzystania (przypisz nowe rezerwacje). Tak więc jednym z rozwiązań zastosowanych w tym przypadku jest użycie puli pamięci do przydzielenia pamięci podręcznej dla obiektów aplikacji. Za każdym razem, gdy potrzebujesz pamięci dla jakiegoś obiektu, po prostu używasz nowego położenia, aby utworzyć nowy obiekt w już zarezerwowanej pamięci.
W ten sposób po uruchomieniu aplikacji masz już zarezerwowaną całą potrzebną pamięć. Wszystkie nowe rezerwacje / wydania pamięci trafiają do przydzielonych pul (możesz mieć kilka pul, po jednej dla każdej innej klasy obiektu). W tym przypadku nie dochodzi do fragmentacji pamięci, ponieważ nie będzie braków, a system może działać bardzo długo (lata) bez cierpienia z powodu fragmentacji.
Widziałem to w praktyce specjalnie dla VxWorks RTOS, ponieważ jego domyślny system alokacji pamięci bardzo cierpi z powodu fragmentacji. Tak więc alokacja pamięci za pomocą standardowej metody new / malloc była zasadniczo zabroniona w projekcie. Wszystkie rezerwacje pamięci powinny być kierowane do dedykowanej puli pamięci.
źródło
Jest używany przez,
std::vector<>
ponieważstd::vector<>
zwykle przydziela więcej pamięci niżobjects
wvector<>
.źródło
Użyłem go do przechowywania obiektów z plikami mapowanymi w pamięci.
Konkretnym przykładem była baza danych obrazów, która przetwarzała bardzo dużą liczbę dużych obrazów (więcej niż mogło zmieścić się w pamięci).
źródło
Widziałem to jako niewielki hack wydajnościowy dla wskaźnika „typu dynamicznego” (w sekcji „Pod maską”):
źródło
void*
zajmuje 8 bajtów. Trochę głupio jest wskazywać ośmiobajtowyvoid*
bajtbool
. Ale całkowicie możliwe jest nałożeniebool
navoid*
, podobnie jakunion { bool b; void* v }
. Potrzebujesz sposobu, aby wiedzieć, że rzecz, którą nazywasz a,void*
to tak naprawdębool
(lub ashort
, lub afloat
, itp.). Artykuł, do którego podłączyłem, opisuje, jak to zrobić. Aby odpowiedzieć na pierwotne pytanie, umiejscowienienew
jest funkcją używaną do tworzeniabool
(lub innego typu), gdzievoid*
oczekiwany jest (rzutowania są używane do późniejszego pobrania / modyfikacji wartości).Użyłem go do tworzenia obiektów opartych na pamięci zawierającej wiadomości otrzymane z sieci.
źródło
Ogólnie rzecz biorąc, nowe miejsce docelowe służy do pozbycia się kosztów alokacji „normalnego nowego”.
Innym scenariuszem, w którym go użyłem, jest miejsce, w którym chciałem mieć dostęp do wskaźnika do obiektu, który jeszcze nie został zbudowany, aby wdrożyć singleton na dokument.
źródło
Może być przydatny podczas korzystania z pamięci współdzielonej, między innymi ... Na przykład: http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions. warunki_anonimowy_przykład
źródło
Jedyne miejsce, na które natknąłem się na to, to pojemniki, które przydzielają ciągły bufor, a następnie wypełniają go odpowiednio do potrzeb obiektami. Jak wspomniano, std :: vector może to zrobić i wiem, że niektóre wersje MFC CArray i / lub CList to zrobiły (ponieważ to tam po raz pierwszy go uruchomiłem). Metoda nadmiernej alokacji buforów jest bardzo przydatną optymalizacją, a nowe umieszczanie jest prawie jedynym sposobem konstruowania obiektów w tym scenariuszu. Czasami jest również używany do konstruowania obiektów w blokach pamięci przydzielonych poza bezpośrednim kodem.
Użyłem go w podobnej pojemności, chociaż nie pojawia się często. Jest to jednak przydatne narzędzie do zestawu narzędzi C ++.
źródło
Silniki skryptów mogą używać go w interfejsie natywnym do przydzielania obiektów natywnych ze skryptów. Przykłady: patrz Angelscript (www.angelcode.com/angelscript).
źródło
Zobacz plik fp.h w projekcie xll na stronie http://xll.codeplex.com Rozwiązuje problem „nieuzasadnionej chumminess z kompilatorem” dla tablic, które lubią nosić ze sobą swoje wymiary.
źródło
Oto użycie zabójcy dla konstruktora lokalnego C ++: wyrównanie do linii pamięci podręcznej, a także innych potęg 2 granic. Oto mój ultraszybki algorytm wyrównywania wskaźnika do dowolnej potęgi 2 granic z 5 lub mniej instrukcjami pojedynczego cyklu :
Czy to nie tylko wywołuje uśmiech na twojej twarzy (:-). I ♥♥♥ C ++ 1x
źródło