Информация об изменениях

Сообщение Re[2]: быстрый new/delete от 08.12.2022 17:59

Изменено 09.12.2022 9:44 watchmaker

Re[2]: быстрый new/delete
Здравствуйте, 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), говорящий в каком режиме деструктор был вызван, и содержит ветвление уже внутри своего тела. Но главное, что там тоже происходят коррекции адресов.
Re[2]: быстрый new/delete
Здравствуйте, 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), говорящий в каком режиме деструктор был вызван, и содержит ветвление уже внутри своего тела. Но главное, что там тоже происходят коррекции адресов.