Re[2]: быстрый new/delete
От: watchmaker  
Дата: 08.12.22 17:59
Оценка: 16 (2) +1
Здравствуйте, fk0, Вы писали:

fk0> Когда ты пишешь delete xxx, то компилятор это превращает в xxx->~XXX(), ::operator delete(xxx).


Это всё же не совсем так: delete xxx — это не синтаксический сахар для xxx->~XXX(); ::operator delete(xxx);.
И если заменить первое на второе, то программы начнут падать.

На самом же деле это только в С++ коде у класса один деструктор, а в скомпилированном коде у класса много деструкторов: например deleting destructor, complete object destructor, base object destructor (в терминологии itanium c++ abi).
И когда ты пишешь delete xxx, то вызывается deleting destructor, а когда пишешь xxx->~XXX(), то вызывается complete object destructor.
Это разные деструкторы именно из-за того, что один нельзя тривиально реализовать через другой, в том числе deleting destructor нельзя выразить как вызов complete object destructor за которым следует вызов ::operator delete(xxx) для того же самого указателя. Пример с виртуальным базовым классом это как раз показывает. И поэтому deleting destructor сначала определяет где была выделена память (для чего нужен неразрушенный объект), потом разрушает поля и базовые классы, и освобождает память для другого указателя, найденного на первом шаге.


P.S. В других ABI (не itanium c++ abi), реализация может чуть отличаться. Например, MSVC генерирует в машинном коде одну функцию, реализующую оба поведения. Но зато эта функция принимает в себя дополнительный параметр (помимо this), говорящий в каком режиме деструктор был вызван, и содержит ветвление уже внутри своего тела. Но главное, что там тоже происходят коррекции адресов.
Отредактировано 09.12.2022 9:44 watchmaker . Предыдущая версия .
Re[3]: быстрый new/delete
От: fk0 Россия https://fk0.name
Дата: 09.12.22 06:49
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, fk0, Вы писали:


fk0>> Когда ты пишешь delete xxx, то компилятор это превращает в xxx->~XXX(), ::operator delete(xxx).


W>Это всё же не совсем так: delete xxx — это не синтаксический сахар для xxx->~XXX(); ::operator delete(xxx);.

W>И если заменить первое на второе, то программы начнут падать.

W>На самом же деле это только в С++ коде у класса один деструктор, а в скомпилированном коде у класса много деструкторов: например deleting destructor, complete object destructor, base object destructor (в терминологии itanium c++ abi).

W>И когда ты пишешь delete xxx, то вызывается deleting destructor, а когда пишешь xxx->~XXX(), то вызывается complete object destructor.
W>Это разные деструкторы именно из-за того, что один нельзя тривиально реализовать через другой, в том числе deleting destructor нельзя выразить как вызов complete object destructor за которым следует вызов ::operator delete(xxx) для того же самого указателя. Пример с виртуальным базовым классом это как раз показывает. И поэтому deleting destructor сначала определяет где была выделена память (для чего нужен неразрушенный объект), потом разрушает поля и базовые классы, и освобождает память для другого указателя, найденного на первом шаге.

Ценное замечание. Можно ли его обобщить, сказав, что в общем случае мы не знаем адрес в памяти, который возвращал
::operator new() когда мы делали new class() ? И, следовательно, вручную вызывать ::operator delete() можно только
если этот адрес нам известен, что могло бы только если ::operator new() вызывался вручную и при этом мы сознательно
отличаем адрес возвращённый ::operator new() и адрес полученный вследствие вызова placement new (с адресом полученным
от ::operator new()), и понимаем, что эти два адреса всегда различны и для последующего вызова деструктора и
::operator delete() мы вынуждены хранить оба адреса. И, следовательно, placement new может в качестве результата
вернуть адрес который отличается от адреса переданного ему в аргументах. Но по чертовской случайности эти оба адреса
часто совпадают, благодаря чему на грабли можно наступить далеко не сразу.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.