base
является Base
объектом; Вы переинтерпретируете его байты как объект Derived
, а затем попытаетесь использовать его, как если бы это был объект Derived
. Поведение, когда вы делаете это, не определено. Ваша программа может зависнуть; может показаться, что он поступает правильно; это может привести к тому, что ваш компьютер загорится.
Обратите внимание, что использование reinterpret_cast
никогда не является правильным для приведения вверх и вниз по иерархии классов. Вы должны использовать static_cast
или dynamic_cast
(или, если вы переходите в базовый класс, приведение не требуется).
Чтобы объяснить, почему вы видите именно это поведение: когда вы вызываете не виртуальную функцию-член (как вы делаете с d->method()
, предполагая, что method
является не виртуальной функцией-членом Derived
), функция, которая вызывается определяется во время компиляции, а не во время выполнения.
Здесь компилятор знает, что d
указывает на объект D
(потому что вы солгали компилятору и сказали, что он есть), поэтому он генерирует код, который вызывает Derived::method()
. Нет никакого «смещения по отношению к указателю» вообще. Вычисление не требуется, поскольку вызываемая функция известна при компиляции программы.
Только при вызове виртуальной функции-члена требуется поиск в таблице (и даже в этом случае поиск требуется только тогда, когда компилятор не знает динамический тип объекта, для которого вызывается функция-член).
Когда вы звоните d->virtual_method()
, вызывается Base::virtual_method
. Зачем? В этой конкретной реализации C ++ первые несколько байтов объекта типа класса, который имеет виртуальные функции-члены ( polymorphic тип класса), содержат тег (называемый «vptr» или «указатель виртуальной таблицы»). ") который идентифицирует фактический тип объекта. Когда вы вызываете виртуальную функцию-член, то во время выполнения этот тег проверяется, и вызываемая функция выбирается на основе этого тега.
Когда вы интерпретируете base
как объект Derived
, вы фактически не изменяете сам объект, поэтому его тег все еще утверждает, что это объект Base
, поэтому и вызывается Base::virtual_method
.
Помните, однако, что все это именно то, что происходит, когда вы компилируете этот код с определенной версией конкретного компилятора. Поведение не определено, и это только один из способов, которым неопределенное поведение может проявиться.