Skip to the content.

Placement new

Оператор new (в отличие от malloc) помимо того что выделяет память, еще вызывает конструктор. Соответственно delete помимо освобождения памяти вызывает деструктор. Такое поведение не всегда нужно, иногда хочется выделять память по-своему (так называемая сырая память), а потом размещать в этой памяти данные, для этого используют особую форму оператора new - placement new (вызывает только конструктор):

// T - любой класс
void * p = MyAlloc(sizeof(T)); // выделили память по-своему, вернулся указатель на область памяти
T * d = new (p) T; // создание экземпляра по адресу p, конструктор по умолчанию
d->~T(); // деструктор прийдёться вызывать самому
MyFree(p); // освобождение памяти по-своему

При этом если нужен конструктор c параметрами либо копирования:

T * d = new (p) T(10);

Однако placement new может вызвать проблемы с выравниванием!

Большой пример:

class T
{
public:
    explicit T(int data = -1) : data(data) {}
private:
    int data;
};

В статической (на стеке) памяти (здесь нет даже намёка на динамическую!!!):

char buff[sizeof(T)];
T * p = new (buff) T(8);
p->~T();

Динамическая память:

void * buff = new char [sizeof(T)];
T * d = new (buff) T;

d->~T();
delete [] (char *)buff;
// почему приведение? delete [] buff; удаляет массив типа Т, а delete [] (char*)buff; массив типа char
// Поскольку мы создавали массив char, то и удалять надо char.

Запись можно сократить до одного указателя:

T * p = (T *)new char[sizeof(T)];
new (p) T(5); // тут для разнообразия вызван конструктор с параметрами

p->~T();
delete [] p;

Массивы:

T * p = (T *)new char [5 * sizeof(T)]; // массив размера 5
for (size_t i = 0; i != 5; ++i)
    new (p + i) T(i);
std::cout << p[2].data << "\n";

Считается что выделять сырую динамическую память более правильно при помощи оператора operator new (функция, которую можно перегрузить):

#include <new>
...

void * buff = operator new(sizeof(T));
T * d = new (buff) T;

d->~T();
operator delete(buff);