Объясните поведение ошибки - PullRequest
0 голосов
/ 30 апреля 2011

Не могли бы вы объяснить, что происходит в этом ошибочном примере:

Base base; Derived* d = reinterpret_cast<Derived*> (&base);
d->method();
d->virtual_method();

//output: Derived-method() Base-virtual_method() 

Я бы ожидал, что этот код будет вести себя наоборот.Вероятно, компилятор использует одну и ту же структуру памяти для Base и Derived, и, конечно, vtable является распространенным явлением.

  • Когда вызывается метод d->, я ожидаю, что компилятор скажет: «Я просто вызываю метод со смещением 0 относительно моего указателя. Указатель указывает на базовый объект, единственный объект вокруг.
  • Когда вызывается d-> virtual_method, компилятор должен сказать, что я собираюсь разрешить его через vtable, и, таким образом, должен быть вызван метод Derived (хотя базовый объект является единственным вокруг, макет расширяетсяк производным).

Итак, я ожидаю увидеть:

//output: Base-method() Derived-virtual_method()

Ответы [ 2 ]

5 голосов
/ 30 апреля 2011

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.

Помните, однако, что все это именно то, что происходит, когда вы компилируете этот код с определенной версией конкретного компилятора. Поведение не определено, и это только один из способов, которым неопределенное поведение может проявиться.

1 голос
/ 30 апреля 2011

Компилятор только выделяет достаточно памяти для хранения запрошенного объекта. Базовое значение может составлять 20 байтов, а в дополнение к этому Derived может быть дополнительными 10 байтами (поэтому размер Derived составляет 30 байтов.)

Когда вы выделяете 20 байтов для Base, а затем (через Derived) позицию 25 байта доступа, она заканчивается концом выделенной памяти, и (в лучшем случае) вы получите сбой.

Компилятор не может выделить 30 байтов для Base, как вы предлагаете, потому что это не только будет расточительно, но Derived может быть реализовано в сторонней библиотеке, и даже может быть неизвестно, когда компилируется Base.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...