programowanieProgramista C++

Co to jest 'placement new' w C++? Do czego i jak jest używany ten mechanizm?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

placement new to szczególna forma operatora new w C++. Pozwala na umieszczanie obiektu pod wskazanym adresem w już przydzielonym obszarze pamięci. Zwykle jest używany do ręcznego zarządzania pamięcią, leży u podstaw działania alokatorów, puli obiektów i serializacji do stałych buforów.

Składnia:

#include <new> void* memory = malloc(sizeof(MyClass)); MyClass* obj = new (memory) MyClass(args...); // wywołuje konstruktor pod adresem memory

Tutaj pamięć jest przydzielana osobno (na przykład przez malloc lub alokator), a następnie konstruktor klasy jest wywoływany w tej pamięci. Nie zapomnij jawnie wywołać destruktora:

obj->~MyClass(); free(memory);

Zalety:

  • Pozwala na realizację własnych alokatorów.
  • Zmniejsza koszty związane z nową alokacją pamięci.
  • Nie wywołuje standardowego alokatora — kontrola nad zarządzaniem pamięcią.

Pytanie z podstępem

Czy ten sam blok pamięci może być używany do umieszczania kilku obiektów w kolejności za pomocą 'placement new', i jakie to wywołuje konsekwencje?

Częsty błędny odpowiedź — można umieszczać nowe obiekty w pamięci, nie dbając o poprzedni stan. W rzeczywistości, jeśli w tej samej przestrzeni pamięci umieszcza się nowy obiekt na starym bez wywołania jego destruktora, dojdzie do wycieku zasobów, a wywołanie destruktora później spowoduje UB.

Przykład:

void* place = malloc(sizeof(A)); A* a = new (place) A(); B* b = new (place) B(); // obiekt A nie został zniszczony! UB!

Przykłady rzeczywistych błędów z powodu nieznajomości subtelności tematu


Historia

W systemie o wysokiej wydajności programista zrealizował pulę obiektów za pomocą malloc + placement new, ale zapomniał wywołać destruktor przy zwracaniu obiektu do puli. W rezultacie zasoby (systemowe deskryptory wewnątrz obiektów) nie były zwalniane, co prowadziło do wycieków i "zawieszania" serwerów.



Historia

W projekcie do serializacji danych binarnych użyto bufora i placement new, aby dekodować obiekty na miejscu. Zapomniano jednak zainicjować pamięć zerami przed umieszczeniem, co doprowadziło do odczytu nieinicjowanych danych i dziwnych błędów podczas pracy z strukturami POD.



Historia

Jeden z modułów migrował na umieszczanie tablic obiektów za pomocą placement new wewnątrz wcześniej przydzielonego bloku. Po dodaniu nowych członków do klasy z nietrywialnymi destruktorami wystąpiły nagłe zakończenia programu — zapomniano, że teraz należy jawnie wywoływać destruktor dla każdego elementu przy czyszczeniu, co wcześniej nie było wymagane.