170 похожих чатов

Всем привет. В одном сегменте кода мне необходимо выполнять ручное

размещение/удаление массива элементов не тривиального типа на вручную выделенной памяти. Тк в проекте поддерживается cpp17 по итогу остановился на std::uninitialized_default_construct_n + std::destroy_n. Создание элементов можно заменить на placement new для массива - конструкторы отрабатывают корректно, но вот что делает и как работает placement delete я не разобрался. Кто-то работал с 'non-allocating placement deallocation functions' или это нечто ненужное и до C++17 необходимо использовать цикл + ручной вызов деструктора по указателю ~T()?

https://en.cppreference.com/w/cpp/memory/new/operator_delete

Вот тут можно посмотреть и запустить код
https://coliru.stacked-crooked.com/view?id=0885bd40bb355840
https://godbolt.org/z/1z6537Kve

12 ответов

21 просмотр

Судя по примечаниям (13)(14) на cppref это не нужно руками звать никогда.

всякие std::destroy_n это сахар в 17++, вот и все. они делают тоже самое, просто сами, ты как бы экономишь время вот и все. если деструктор нужен, да ты его должен вызвать. всякие алигн стораджы и юнионы, используют по большей части во всяких эмбедетах, когда память всегда четко распределена, но возможно не известно под какие драйвера(девайсы) тебе как бы из внешки прилетит инфа. мол 10 датчиков дыма 15 пожарных. ты делаешь placement new.

0) я не писал продакшн код ни с одной из этих фич С++ и его стандартной библиотеки, но провел прилично времени за главами стандарта про объектную модель 1) вызов недеаллоцирующего operator delete[]() (не путать с delete expression) ничего не делает и никакую семантику в себе не несет. то есть вы можете его вызвать, и вам ничего за это не будет, но он не закончит время жизни объектов, созданных new, как вы можете от него ожидать 2) смысл существования этой перегрузки operator delete[]() изложен в стандарте. от себя могу добавить предположение, что и симметрии ради с неаллоцирующим operator new[]() 3) механизм работы placement delete постигнуть невозможно, так как такого выражения не существует — delete expression не предоставляет возможность передавать дополнительные аргументы в функцию деаллокации, в отличие от new 4) создавая объекты A через placement array new, вы не переиспользуете отдельные aligned_storage под хранение объектов A, а переиспользуете сам массив aligned_storag'ей 5.1) разница в том, что в этом случае вы не можете использовать арифметику указателей на основе place для того, чтобы получать указатели на объекты A, потому что массива place больше нет. для этого годится только указатель, который вернет new[] (и который вы игнорируете), либо отмытый (std::launder) ptr или любой другой указатель на начало place. 5.2) а еще это выстрелит, если вы усилите требования к выравниванию aligned_storage (например, чтобы выравнивать по 64-байтовым кэш-линиям), потому что нет возможности передать выравнивание в placement new[] 6) ваше С++17 решение совершенно верное, и если вам нужно портировать его туда, где эти алгоритмы недоступны, то проще и надежнее всего написать руками то же, что они делают под капотом, а именно вызывают placement new для каждого aligned_storage и так же по одному вызывают деструкторы A потом 7) возможно, вы уже знаете или поняли к этому моменту, что даже после успешного вызова std::uninitialized_default_construct_n вы не можете считать place массивом объектов А и заниматься арифметикой указателей на основе этого предположения — он остается массивом aligned_storage 8) у меня нет внятного ответа, почему в этом случае delete[] творит такую дичь. в стандарте я вижу лишь, что он должен работать. видимо, как обычно плохо смотрю возможно, @ddvamp может что-то добавить

Евгений-Пройдаков Автор вопроса
Vlad
0) я не писал продакшн код ни с одной из этих фич ...

Влад, спасибо за развёрнутый комментарий. По пункту 7 я понимаю, что если выравнивание будет отличаться, то все взлетит на воздух и нужно отдельно использовать каждую ячейку стороджа.

Евгений Пройдаков
Влад, спасибо за развёрнутый комментарий. По пункт...

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

Евгений-Пройдаков Автор вопроса
Vlad
правда, это UB даже при совпадающем выравнивании. ...

typename std::aligned_storage<1024 * sizeof(T), alignof(T)>::type data; так было-бы лучше? Реальный код использует результат std::allocator или custom где память уже выровнена по странице да и объекты сами по размеру кеш линии.

Евгений Пройдаков
typename std::aligned_storage<1024 * sizeof(T), al...

чтобы потом сделать new (data) T[1024]? может, и прокатило бы, если бы вы писали под 20 стандарт и были довольны стандартным operator new[](std​::​size_­t, void*). если эти условия не соблюдаются, то вы упираетесь в стену имя которой array allocation overhead лучше всего взять С++17 решение, и если нужно обращаться по индексу, то написать простейшую обертку над reinterpret_cast'ом (godbolt). всевозможные проверки оставил за скобками

Vlad
0) я не писал продакшн код ни с одной из этих фич ...

По поводу п.8: в примере для delete expression не работает отбрасывание вызова deallocation function http://eel.is/c++draft/expr.delete#7.3, т.к. allocation function является omitted только для constant expression http://eel.is/c++draft/expr.new#13 Как следствие получаем UB из-за нарушения предусловия deallocation function http://eel.is/c++draft/new.delete.array#9

Alexander Tulikov
По поводу п.8: в примере для delete expression не ...

я согласен, что оно так получается в итоге, но мне не понятно, зачем она вызывается (и зачем ее считать вызванной) для new, которому не нужно выделять память http://eel.is/c++draft/expr.new#10

Vlad
я согласен, что оно так получается в итоге, но мне...

Как вариант потому, что пользователь может её заменить на аналог с дополнительным функционалом (зануление памяти то же). А в прикладном смысле не понятно, откуда брать информацию о том, размещающий был вызов new или аллоцирующий.

Alexander Tulikov
Как вариант потому, что пользователь может её заме...

вариант с заменой звучит интересно, но представляется проблематичным в реализации ввиду этого пункта

Vlad
вариант с заменой звучит интересно, но представляе...

Это относится только к глобальным функциям, на уровне класса их заменять можно.

Похожие вопросы

Обсуждают сегодня

а через ESC-код ?
Alexey Kulakov
29
30500 за редактор? )
Владимир
47
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
13
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
в JclConsole объявлено так: function CtrlHandler(CtrlType: DWORD): BOOL; stdcall; - где ваше объявление с stdcall? у вас на картинке нет stdcall
Karagy
8
Ребят в СИ можно реализовать ООП?
Николай
33
Вот еще странный косяк, подскажите как бороться. Я git clone сделал себе всего embassy и примеры там запускаю. Всё хорошо. Но вот решил в cargo.toml зависимости не как в приме...
Lukutin R2AJP
2
program test; {$mode delphi} procedure proc(v: int32); overload; begin end; procedure proc(v: int64); overload; begin end; var x: uint64; begin proc(x); end. Уж не знаю...
notme
6
Карта сайта