Создание интеллектуального указателя (или любого объекта-должника) временным является плохим проектом.
Эта проблема дизайна приводит к неправильному управлению временем жизни, в частности к разрушению объекта, который все еще используется.Это вызывает неопределенное поведение;неопределенное поведение по определению не определено или даже не ограничено стандартом (оно может быть ограничено другими принципами, инструментами, устройствами).
Мы все еще можем попытаться понять, как код с UB транслируется на практике во многихслучаев. специфическое поведение , которое вы наблюдаете:
, которое печатает:
FirstLevel
FirstLevel
, безусловно, вызвано интерпретацией памяти, оставленной уничтоженным объектом, как будтоэто был живой объект;поскольку эта память не использовалась повторно (по случайности, и любое изменение в программе или реализации может нарушить это свойство), вы видите объект в состоянии, в котором он находился во время уничтожения.
В деструкторевызовы виртуальных функций разрушаемого объекта всегда преобразуются в переопределитель функции в классе деструктора: внутри Base::~Base
, вызов foo()
разрешается в Base::foo()
;компилятор, который использует vptrs и vtables (на практике все компиляторы), гарантирует, что виртуальные вызовы разрешаются таким образом, путем сброса vptr в vtable для Base
в начале выполнения деструктора базового класса.
Итак, вы видите, что vptr все еще указывает на базовый класс vtable.
Конечно, реализация отладки имеет право установить для vptr какое-то другое значение в конце деструктора базового класса.чтобы убедиться, что попытка вызова виртуальных функций на уничтоженном объекте не дает четкого и однозначного результата.