C-стиль литье, в данном случае из-за отношения наследования, эквивалентно static_cast
. Как и в большинстве приведений (за исключением dynamic_cast
, где вводятся некоторые проверки), когда вы сообщаете, что объект действительно Circle
, компилятор будет доверять вам и предположить, что он есть. Поведение undefined в этом случае, поскольку объект не a Circle
, вы лжете компилятору и все ставки отключены.
Что действительно происходит здесь, так это то, что компилятор показывает, есть ли смещение от базы до производного типа для этой комбинации и соответственно отрегулируйте указатель. На этом этапе вы получаете указатель на производный тип, который имеет отрегулированный адрес, и тип безопасности отключен от окна. Любой доступ через этот указатель будет предполагать, что память в этом месте - это то, что вы ему сказали, и будет интерпретировать его как таковое, что является неопределенным поведением, поскольку вы читаете память, как если бы это был тип, которого нет.
Когда указатель скорректирован?
struct base1 { int x; };
struct base2 { int y; };
struct derived : base1, base2 {};
base2 *p = new derived;
Адрес derived
, base1
и base1::x
одно и то же, но отличается от адреса base2
и base2::y
. Если вы делали от derived
до base2
, компилятор отрегулировал бы указатель в конверсии (добавив sizeof(base1)
к адресу), при отливке от base2
до derived
компилятор будет настраиваться в противоположном направлении.
Почему вы получаете результаты, которые получаете?
Форма: Draw Метод
Круг: Только CIRCLE имеет этот
Это связано с тем, как динамическая отправка осуществляется компилятором. Для каждого типа с хотя бы одной виртуальной функцией компилятор будет генерировать одну (или несколько) виртуальных таблиц. Виртуальная таблица содержит указатели на конечный указатель для каждой функции в типе. Каждый объект содержит указатель (ы) для виртуальной таблицы (таблиц) для полного типа. Вызов виртуальной функции включает компилятор, выполняющий поиск в таблице и следуя указателю.
В этом случае объект действительно является Shape
, vptr будет ссылаться на виртуальную таблицу для Shape
. Когда вы отбрасываете от Shape
до Derived
, вы сообщаете компилятору, что это Circle
(даже если это не так). Когда вы вызываете draw()
, компилятор следует за vptr (в этом случае vptr для подобъекта Shape
и подобъекта Circle
находятся в том же смещении (0 в большинстве ABI) от начала объекта. Вызов, введенный компилятором, следует Shape
vptr (бросок делает не изменения каких-либо содержимое памяти, что vptr еще у Shape
) и ударил Shape::draw
.
В случае display()
вызова не происходит динамически отправляется через vptr, как это а не виртуальная функция. Это означает, что компилятор будет вводить прямой вызов Circle::draw()
, передавая адрес, который у вас есть как указатель this
. Вы можете имитировать thi s для виртуальной функции путем отключения динамической диспетчеризации:
ptrCircle1->Circle::draw();
Помните, что это всего лишь объяснение деталей компилятора, которые ускользают стандарт C++, по стандарту это только Неопределенное поведение, независимо компилятор делает это хорошо , Другой компилятор мог бы сделать что-то другое (хотя все ABI, которые я видел, в основном одинаковы здесь).
Если вы действительно заинтересованы в деталях, как это работает, вы можете взглянуть на Внутри объектной модели C++ от Lippman. Это старая книга, но она решает проблемы, которые должен решить компилятор, и некоторые из решений, которые использовали компиляторы.
Вы сталкиваетесь с неопределенным поведением, что литье является незаконным. – Mat
«Неопределенное поведение» означает, что работа является допустимым результатом. :) Если вы хотите сделать это сбоем, добавьте переменную-член в Circle и ссылайтесь на нее с display(). Это, вероятно, сделает это, но все равно не гарантирует. – dlf
Вы хотите, чтобы я не использовал C-Style? – StackIT