ПрограммированиеC++ разработчик

Что такое 'placement new' в C++? Для чего и как используется этот механизм?

Проходите собеседования с ИИ помощником Hintsage

Ответ

placement new — это особая форма оператора new в C++. Она позволяет размещать объект по заданному адресу в уже выделенной области памяти. Обычно используется для ручного управления памятью, лежит в основе работы аллокаторов, пулинга объектов и сериализации в фиксированные буферы.

Синтаксис:

#include <new> void* memory = malloc(sizeof(MyClass)); MyClass* obj = new (memory) MyClass(args...); // вызывает конструктор по адресу memory

Здесь память выделяется отдельно (например, через malloc или аллокатор), а затем конструктор класса вызывается в этой памяти. Не забудьте явно вызвать деструктор:

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

Плюсы:

  • Позволяет реализовать собственные аллокаторы.
  • Уменьшаются расходы на новую аллокацию памяти.
  • Не вызывает стандартный аллокатор — контроль над управлением памятью.

Вопрос с подвохом

Может ли один и тот же блок памяти использоваться для размещения нескольких объектов подряд с помощью 'placement new', и к каким последствиям это приводит?

Частый неверный ответ — можно размещать новые объекты в памяти, не заботясь о предыдущем состоянии. На самом деле, если в той же области памяти размещать новый объект поверх старого без вызова его деструктора, произойдет утечка ресурсов, и вызов деструктора потом приведёт к UB.

Пример:

void* place = malloc(sizeof(A)); A* a = new (place) A(); B* b = new (place) B(); // объект A не разрушен! UB!

Примеры реальных ошибок из-за незнания тонкостей темы


История

В высоконагруженной системе разработчик реализовал пул объектов через malloc + placement new, но забыл вызывать деструктор при возврате объекта в пул. В итоге ресурсы (системные дескрипторы внутри объектов) не освобождались, приводя к утечкам и "зависанию" серверов.



История

В проекте для сериализации бинарных данных использовался буфер и placement new, чтобы декодировать объекты на месте. Однако забыли инициализировать память нулями перед размещением, что привело к чтению неинициализированных данных и странным багам при работе с POD-структурами.



История

Один из модулей мигрировал на размещение массивов объектов с помощью placement new внутри заранее выделенного блока. После добавления внутри класса новых членов с нетривиальными деструкторами возникли аварийные завершения программы — забыли, что теперь нужно явно вызывать деструктор для каждого элемента при очистке, чего раньше не требовалось.