Что такое placement new в C++?

«Что такое placement new в C++?» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Placement new — это особая форма оператора new, которая не выделяет память, а конструирует объект по заранее предоставленному адресу. Это низкоуровневая операция, используемая для разделения этапов выделения памяти и инициализации объекта.

Основные отличия от обычного new:

  1. Не выделяет память: Вы сами управляете областью памяти (стек, выделенный ранее буфер, shared memory).
  2. Не выбрасывает std::bad_alloc: Поскольку память уже есть, исключение на этапе размещения не генерируется (но конструктор объекта может выбросить своё исключение).
  3. Требует ручного вызова деструктора: Память не освобождается оператором delete, деструктор нужно вызывать явно.

Синтаксис: new (адрес_памяти) Тип(аргументы_конструктора)

Пример использования:

#include <new> // Необходим для placement new
#include <iostream>

class MyClass {
public:
    int value;
    MyClass(int v) : value(v) {
        std::cout << "Constructor called. Value: " << value << 'n';
    }
    ~MyClass() {
        std::cout << "Destructor called.n";
    }
};

int main() {
    // 1. Выделяем память (например, в стеке или из пула)
    alignas(MyClass) char buffer[sizeof(MyClass)];

    // 2. Конструируем объект в выделенной памяти с помощью placement new
    MyClass* obj = new (buffer) MyClass(42);
    std::cout << "Object value: " << obj->value << 'n'; // 42

    // 3. Явно вызываем деструктор (обязательный шаг!)
    obj->~MyClass();

    // Память `buffer` будет освобождена при выходе из области видимости.
    return 0;
}

Типичные сценарии применения:

  • Оптимизация производительности: Создание объектов в заранее выделенном пуле памяти (memory pool) для избежания фрагментации кучи и ускорения аллокаций.
  • Низкоуровневое программирование: Работа с разделяемой памятью (shared memory), аппаратными регистрами или памятью, выделенной средствами C (malloc).
  • Контейнеры: Реализация стандартных контейнеров (например, std::vector), которые выделяют сырую память блоками и затем конструируют в ней объекты.

Важное предупреждение: Необходимо следить за выравниванием (alignment) предоставляемой памяти. Использование alignas или специализированных аллокаторов помогает избежать ошибок выравнивания (misalignment), ведущих к падению производительности или краху программы на некоторых архитектурах.