# placement new для массивов Вам посчастливилось добыть новую суперэффективную библиотеку для управления памятью? Вы хотите пользоваться ею в C++ и не сталкиваться с надуманным UB из-за проблем с лайфтаймами? Вам повезло! Просто выделяйте память своей библиотекой, создавайте в выделенном буфере объекты с помощью placement new и забот не знайте! ```C++ void* buffer = my_external_malloc(sizeof(T), alignof(T)); auto pobj = new (buffer) T(); ``` Красиво, просто, здорово! А что если мы захотим выделить память и разместить в ней массив? Нет ничего проще! ```C++ void* buffer = my_external_malloc(n * sizeof(T), alignof(T)); auto pobjarr = new (buffer) T[n]; ``` Все, можно идти пить чай. Задача решена. Мы молодцы. Как похорошел C++ с 11-го стандарта! Но не может же быть все так просто? Конечно же нет! До C++20 вариант placement new для массивов имеет полное право испоганить вашу память. Конструкция ```C++ new (buffer) T[n]; ``` согласно примерам (§ 8.5.2.4 (15.4)) из стандарта [C++17](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf), переводится в ```C++ operator new[](sizeof(T) * n + x, buffer); // или operator new[](sizeof(T) * n + x, std::align_val_t(alignof(T)), buffer); ``` Где `x` — никак не специфицируемое неотрицательное число, предназначенное, например, чтобы застолбить место под какую-либо метаинформацию о выделенном массиве: засунуть число элементов в начало области памяти или расставить маркеры начала/конца или еще что-нибудь, что обычно делают аллокаторы. То есть placement new для массива вполне может полезть за пределы предоставленного вами буфера. Очень удобно! В C++20 восхитительную [формулировку](https://eel.is/c++draft/expr.new#19.4) изменили. Теперь же, если конструкция ```C++ new (arg1, arg2...) T[n]; ``` соответствует вызову стандартного ```C++ void* operator new[]( std::size_t count, void* ptr); ``` То все будет хорошо. Никаких магических сдвигов на `+x` не возникнет. Но если же какой-то доброжелатель определил свой собственный operator placement new... Впрочем, это уже совсем другая история... -------------- Я не встречал ни одного компилятора, и ни одной поставки стандартной библиотеки, в которых стандартный placement new как-либо двигал указатель на пользовательский буфер. Реальную угрозу трудноотлавливаемого UB в большей степени представляют user-defined версии placement new. Чтобы обезопасить себя и вызвать настоящий стандартный placement new, нужно использовать `::new` и кастить указатель на буфер к `void*`. Либо положиться на алгоритмы `std::uninitialized_default_construct_n` и подобные ему. Также нужно отметить, что в C++ нет placement delete синтаксиса. Мы можем только явно вызвать `operator delete[](void* ptr, void* place)`, стандартная версия которого ничего не делает. Тут, конечно, нужно понимать разницу между самим `operator delete` и синтаксическими конструкциями `delete p` и `delete [] p`. Первый занимается только управлением памятью. Последние же — еще и вызывают деструкторы. В C++ нет именно отдельной синтаксической конструкции, чтобы махом вызывать деструкторы элементов массива, созданного с помощью placement new. Это нужно делать вручную или использовать алгоритм `std::destroy`. Ни в коем случае не стоит использовать `delete []` против указателя, полученного с помощью placement new []. Будет [плохо](https://godbolt.org/z/WeWPseKoG).