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