Jakie są zastosowania dla "umieszczenia nowego"?

Czy ktoś tu kiedyś używał C++ ' S "placement new"? Jeśli tak, to po co? Wydaje mi się, że przydałby się tylko na sprzęcie mapowanym pamięcią.

Author: CharlesB, 2008-10-21

22 answers

Placement new pozwala na zbudowanie obiektu na pamięci, która jest już przydzielona.

Możesz chcieć to zrobić dla optymalizacji (szybciej jest nie przeznaczyć cały czas), ale musisz ponownie skonstruować obiekt wiele razy. Jeśli musisz zachować realokację, może być bardziej wydajne przydzielanie więcej niż potrzebujesz, nawet jeśli nie chcesz jeszcze z niej korzystać.

Devex daje dobry przykład :

Standard C++ obsługuje również umieszczenie nowe operator, który konstruuje obiekt na wstępnie przydzielonym buforze. To jest przydatny przy budowaniu puli pamięci, garbage collector lub po prostu kiedy wydajność i bezpieczeństwo WYJĄTKÓW to paramount (there ' s no danger of awaria alokacji pamięci został już przydzielony, oraz konstruowanie obiektu na wstępnie przydzielony bufor zajmuje mniej czasu):

char *buf  = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi");    // placement new
string *q = new string("hi");          // ordinary heap allocation

Możesz również chcieć mieć pewność, że nie może dojść do niepowodzenia alokacji w określonej części kodu krytycznego (może pracujesz np. na rozruszniku serca). W takim przypadku chcesz użyć placement new.

Dealokacja w lokacie new

Nie należy dealokować każdego obiektu używającego bufora pamięci. Zamiast tego należy usunąć [] tylko oryginalny bufor. Będziesz musiał ręcznie wywoływać destruktory bezpośrednio ze swoich klas. Aby uzyskać dobrą sugestię na ten temat, zobacz FAQ Stroustrupa na: czy istnieje "umieszczenie Usuń" ?

 325
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-23 04:02:39

Używamy go z niestandardowymi pulami pamięci. Tylko szkic:

class Pool {
public:
    Pool() { /* implementation details irrelevant */ };
    virtual ~Pool() { /* ditto */ };

    virtual void *allocate(size_t);
    virtual void deallocate(void *);

    static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};

class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };

// elsewhere...

void *pnew_new(size_t size)
{
   return Pool::misc_pool()->allocate(size);
}

void *pnew_new(size_t size, Pool *pool_p)
{
   if (!pool_p) {
      return Pool::misc_pool()->allocate(size);
   }
   else {
      return pool_p->allocate(size);
   }
}

void pnew_delete(void *p)
{
   Pool *hp = Pool::find_pool(p);
   // note: if p == 0, then Pool::find_pool(p) will return 0.
   if (hp) {
      hp->deallocate(p);
   }
}

// elsewhere...

class Obj {
public:
   // misc ctors, dtors, etc.

   // just a sampling of new/del operators
   void *operator new(size_t s)             { return pnew_new(s); }
   void *operator new(size_t s, Pool *hp)   { return pnew_new(s, hp); }
   void operator delete(void *dp)           { pnew_delete(dp); }
   void operator delete(void *dp, Pool*)    { pnew_delete(dp); }

   void *operator new[](size_t s)           { return pnew_new(s); }
   void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
   void operator delete[](void *dp)         { pnew_delete(dp); }
   void operator delete[](void *dp, Pool*)  { pnew_delete(dp); }
};

// elsewhere...

ClusterPool *cp = new ClusterPool(arg1, arg2, ...);

Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);

Teraz możesz klasterować obiekty w jednej arenie pamięci, wybrać alokator, który jest bardzo szybki, ale nie powoduje dealokacji, używać mapowania pamięci i innych semantycznych, które chcesz narzucić, wybierając pulę i przekazując ją jako argument do nowego operatora umieszczania obiektu.

 50
Author: Don Wakefield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 17:21:03

Jest to przydatne, jeśli chcesz oddzielić alokację od inicjalizacji. STL wykorzystuje placement new do tworzenia elementów kontenera.

 45
Author: MSN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 15:07:28

Używałem go w programowaniu w czasie rzeczywistym. Zazwyczaj nie chcemy wykonywać żadnej dynamicznej alokacji (lub dealokacji) 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ść, czego klasa może wymagać). Następnie, gdy dowiem się w czasie wykonywania, jak konstruować rzeczy, placement new może być używany do konstruowania obiektów dokładnie tam, gdzie chcę. Jedną z sytuacji, w której jej użyłem, była pomoc w stworzeniu niejednorodnego okrągłego bufora .

To z pewnością nie jest dla osób o słabym sercu, ale dlatego robią składnię dla tego trochę szorstkie.

 27
Author: T.E.D.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 15:00:52

Używałem go do konstruowania obiektów przydzielonych na stosie za pomocą alloca ().

Bezwstydna wtyczka: pisałem o niej tutaj .

 20
Author: Ferruccio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-21 14:57:54

Head Geek: BINGO! Masz go całkowicie - dokładnie do tego jest idealny. W wielu środowiskach wbudowanych zewnętrzne ograniczenia i / lub ogólny scenariusz użycia zmusza programistę do oddzielenia alokacji obiektu od jego inicjalizacji. C++ wywołuje tę "instancjację" , ale ilekroć akcja konstruktora musi być jawnie wywoływana bez dynamicznej lub automatycznej alokacji, umieszczenie nowego jest sposobem na to. Jest to również doskonały sposób na zlokalizowanie globalnego C++ obiekt 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.

 12
Author: dorcsssc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-16 17:47:32

Użyłem go do stworzenia klasy Variant (tj. obiektu, który może reprezentować pojedynczą wartość, która może być jednym 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), to znaczona Unia w stylu C jest wystarczająca, ale jeśli chcesz, aby niektóre typy wartości były obiektami C++ (np. std::string), funkcja C union nie będzie działać, ponieważ nie-pod typy danych mogą nie być deklarowane jako część Unii.

Więc zamiast przydziel tablicę bajtów, która jest wystarczająco duża(np. sizeof (the_largest_data_type_i_support)) i użyj placement new, aby zainicjować odpowiedni obiekt C++ w tym obszarze, gdy wariant jest ustawiony na wartość tego typu. (I umieszczenie Usuń wcześniej przy przełączaniu się z innego typu danych nie-POD, oczywiście)

 11
Author: Jeremy Friesner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-23 05:10:02

Jest to również przydatne, gdy chcesz ponownie zainicjować globalne lub statycznie przydzielone struktury.

Stara metoda C używała memset(), aby ustawić wszystkie elementy na 0. Nie można tego zrobić w C++ z powodu vtables i niestandardowych konstruktorów obiektów.

Więc czasami używam następujących

 static Mystruct m;

 for(...)  {
     // re-initialize the structure. Note the use of placement new
     // and the extra parenthesis after Mystruct to force initialization.
     new (&m) Mystruct();

     // do-some work that modifies m's content.
 }
 9
Author: nimrodm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-04 08:29:12

Jest to przydatne, gdy budujesz jądro - gdzie umieszczasz kod jądra odczytany z dysku lub z pagetable? Musisz wiedzieć, gdzie skoczyć.

Lub w innych, bardzo rzadkich okolicznościach, takich jak gdy masz mnóstwo przydzielonego miejsca i chcesz umieścić kilka struktur za sobą. Mogą być pakowane w ten sposób bez potrzeby użycia operatora offsetof (). Na to też są inne sztuczki.

Uważam również, że niektóre implementacje STL wykorzystują umieszczenie nowy, jak std:: vector. Przydzielają miejsce na 2 ^ N elementów w ten sposób i nie muszą zawsze realloc.

 8
Author: mstrobl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 16:38:02

Umieszczenie nowego jest również bardzo przydatne podczas serializacji (powiedzmy za pomocą boost::serialization). Przez 10 lat c++ to dopiero drugi przypadek, do którego potrzebowałem nowego (trzeci, jeśli uwzględnisz wywiady :)).

 8
Author: RichardBruce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-05 00:45:23

Myślę, że nie zostało to podkreślone przez żadnej odpowiedzi, ale innym dobrym przykładem i zastosowaniem dla nowe umieszczenie {[2] } jest zmniejszenie fragmentacji pamięci (za pomocą puli pamięci). Jest to szczególnie przydatne w systemach wbudowanych i 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 mieć fragmentacji. Ten problem nie ma nic wspólnego z wyciekiem pamięci.

Nawet gdy bardzo dobry malloc implementacja jest używana (lub podobna funkcja zarządzania pamięcią) bardzo trudno jest poradzić sobie z fragmentacją przez długi czas. W pewnym momencie, jeśli nie uda Ci się sprytnie zarządzać rezerwacją pamięci / zwolnieniem połączeń, możesz skończyć z wieloma małymi lukami, które są trudne do ponownego wykorzystania (przypisanie do nowych rezerwacji). Tak więc jednym z rozwiązań, które są używane w tym przypadku, jest użycie puli pamięci do przydzielenia wcześniej pamięci dla obiektów aplikacji. After-wards za każdym razem, gdy potrzebujesz pamięci dla jakiś obiekt wystarczy użyć new placement , aby utworzyć nowy obiekt na już zarezerwowanej pamięci.

W ten sposób, po uruchomieniu aplikacji masz już zarezerwowaną całą potrzebną pamięć. Cała nowa rezerwacja/zwolnienie pamięci trafia do przydzielonych pul (możesz mieć kilka pul, po jednej dla każdej innej klasy obiektu). W tym przypadku nie występuje fragmentacja pamięci, ponieważ nie będzie luk, a Twój system może działać przez bardzo długi czas (lata) bez cierpienia fragmentacja.

Widziałem to w praktyce specjalnie dla VxWorks RTO, ponieważ jego domyślny system alokacji pamięci bardzo cierpi z powodu fragmentacji. Tak więc przydzielanie pamięci za pomocą standardowej metody new / malloc było zasadniczo zabronione w projekcie. Wszystkie rezerwacje pamięci powinny trafić do dedykowanej puli pamięci.

 7
Author: rkachach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-01 13:54:57

Jest używany przez std::vector<>, ponieważ std::vector<> zazwyczaj przydziela więcej pamięci niż jest objects w vector<>.

 7
Author: Andreas Magnusson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-14 22:12:17

Używałem go do przechowywania obiektów z plikami mapowanymi 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ż mieściło się w pamięci).

 6
Author: Martin Beckett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 16:38:15

Używałem go do tworzenia obiektów w oparciu o pamięć zawierającą wiadomości odebrane z sieci.

 6
Author: Steve Fallows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 17:01:14

Widziałem go jako lekki hack wydajności dla wskaźnika" dynamic type " (w sekcji "pod maską"):

Ale oto Trick, którego użyłem, aby uzyskać szybką wydajność dla małych typów: jeśli trzymana wartość może zmieścić się wewnątrz pustki*, nie przeszkadza mi przydzielanie nowego obiektu, zmuszam go do samego wskaźnika za pomocą placement new.

 6
Author: Max Lybbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 20:45:16

Ogólnie rzecz biorąc, placement new 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 miał być jeszcze skonstruowany, aby zaimplementować singleton dla danego dokumentu.

 5
Author: xtofl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 17:32:33
 5
Author: kralyk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-28 20:52:39

W rzeczywistości wymagane jest zaimplementowanie dowolnej struktury danych, która przydziela więcej pamięci niż minimalnie wymagana dla liczby wstawionych elementów (tj. cokolwiek innego niż powiązana struktura, która przydziela jeden węzeł na raz).

Weź pojemniki jak unordered_map, vector, lub deque. Wszystkie one przydzielają więcej pamięci niż jest to minimalnie wymagane dla wstawionych do tej pory elementów, aby uniknąć konieczności alokacji sterty dla każdego pojedynczego wstawiania. Użyjmy vector jako najprostszy przykład.

Kiedy robisz:

vector<Foo> vec;

// Allocate memory for a thousand Foos:
vec.reserve(1000);

... to nie konstruuje tysiąca stóp. Po prostu przydziela / rezerwuje dla nich pamięć. Gdyby vector nie używało tu placetion new, byłoby to domyślne-konstruowanie Foos w każdym miejscu, a także wywoływanie ich destruktorów nawet dla elementów, których nigdy nie wstawiłeś.

Przydział !/ Align = "Left" / = Destruction

Ogólnie rzecz biorąc, aby wdrożyć wiele struktur danych, takich jak powyższe, nie można traktować alokacji pamięci i konstruowania elementów jako jednej niepodzielnej rzeczy, a także nie można traktować uwalniania pamięci i niszczenia elementów jako jednej niepodzielnej rzeczy.

Musi istnieć oddzielenie między tymi ideami, aby uniknąć niepotrzebnego wywoływania konstruktorów i destruktorów w lewo i w prawo, i dlatego biblioteka standardowa oddziela ideę std::allocator (która nie konstruuje ani nie niszczy elementów, gdy przydziela / zwalnia pamięć*) z kontenerów, które jej 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ę projektowania std::allocator, ale to inny temat, o którym Nie będę narzekać. :- D

W każdym razie, używam go często, ponieważ napisałem wiele kontenerów C++ zgodnych ze standardami ogólnego przeznaczenia, które nie mogą być zbudowane pod względem istniejące. Wśród nich jest mała implementacja wektorowa, którą zbudowałem kilka dekad temu, aby uniknąć alokacji sterty w typowych przypadkach, oraz wydajna pamięć trie (nie przydziela jednego węzła na raz). W obu przypadkach nie mogłem ich zaimplementować 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.

Naturalnie, jeśli kiedykolwiek będziesz pracować z niestandardowymi alokatorami do przydzielania obiektów każdy z nich, podobnie jak darmowa lista, powinien również użyć placement new, w ten sposób (podstawowy przykład, który nie przeszkadza w wyjątkach-safety lub RAII):

Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);
 5
Author: Team Upvote,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-16 03:49:26

Jedyne miejsce, na które natknąłem się, to kontenery, które przydzielają sąsiadujący bufor, a następnie wypełniają go obiektami zgodnie z wymaganiami. Jak już wspomniałem, std:: vector może to zrobić, I Wiem, że niektóre wersje MFC CArray i/lub CList to zrobili(bo tam po raz pierwszy natknąłem się na to). Metoda nadmiernej alokacji bufora jest bardzo użyteczną optymalizacją, a umieszczenie nowego jest prawie jedynym sposobem konstruowania obiektów w tym scenariuszu. Jest również czasami używany do konstruowania obiektów w blokach pamięci przydzielone poza twoim bezpośrednim kodem.

Używałem go w podobnym charakterze, chociaż nie pojawia się często. Jest to jednak przydatne narzędzie dla C++ toolbox.

 4
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 18:13:23

Silniki skryptów mogą używać go w natywnym interfejsie do przydzielania natywnych obiektów ze skryptów. Zobacz Angelscript (www.angelcode.com/angelscript) na przykład.

 4
Author: Raindog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-21 21:27:04

Patrz PR.plik h W projekcie xll w http://xll.codeplex.com rozwiązuje problem "nieuzasadnionej chumminess z kompilatorem" dla tablic, które lubią nosić ze sobą swoje wymiary.

typedef struct _FP
{
    unsigned short int rows;
    unsigned short int columns;
    double array[1];        /* Actually, array[rows][columns] */
} FP;
 3
Author: Keith A. Lewis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-26 20:03:32

Oto zabójcze użycie konstruktora C++ in-place: wyrównywanie do linii pamięci podręcznej, a także inne uprawnienia 2 granic. Oto Mój ultraszybki algorytm wyrównania wskaźnika do dowolnej potęgi 2 granic z 5 lub mniej instrukcjami jednotorowymi :

/* Quickly aligns the given pointer to a power of two boundary IN BYTES.
@return An aligned pointer of typename T.
@brief Algorithm is a 2's compliment trick that works by masking off
the desired number in 2's compliment and adding them to the
pointer.
@param pointer The pointer to align.
@param boundary_byte_count The boundary byte count that must be an even
power of 2.
@warning Function does not check if the boundary is a power of 2! */
template <typename T = char>
inline T* AlignUp(void* pointer, uintptr_t boundary_byte_count) {
  uintptr_t value = reinterpret_cast<uintptr_t>(pointer);
  value += (((~value) + 1) & (boundary_byte_count - 1));
  return reinterpret_cast<T*>(value);
}

struct Foo { Foo () {} };
char buffer[sizeof (Foo) + 64];
Foo* foo = new (AlignUp<Foo> (buffer, 64)) Foo ();
Czy to nie wywołuje uśmiechu na twojej twarzy (: -). I ♥ ♥ ♥ C++1x
 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-30 02:00:02