Подтвердить что ты не робот

Как реализована динамическая реализация

Рассмотрим эту простую иерархию:

class Base { public: virtual ~Base() { } };
class Derived : public Base { };

Попытка спускать Base* p в Derived* возможна с помощью dynamic_cast<Derived*>(p). Я думал, что dynamic_cast работает, сравнивая указатель vtable в p с тем, что находится в объекте Derived.

Но что, если мы выведем другой класс из Derived? Теперь имеем:

class Derived2 : public Derived { };

В этом случае:

Base* base = new Derived2;
Derived* derived = dynamic_cast<Derived*>(base);

Мы по-прежнему получаем успешное нажатие, хотя указатель vtable в Derived2 не имеет ничего общего с указателем vtable в Derived.

Как это работает? Как может dynamic_cast знать, был ли Derived2 получен из Derived (что, если Derived было объявлено в другой библиотеке)?

I am ищет конкретные сведения о том, как это работает (желательно в GCC, но другие тоже прекрасны). Этот вопрос не дубликат этого вопроса (который не указывает, как он работает).

4b9b3361

Ответ 1

Как dynamic_cast знает, был ли Derived2 получен из Derived (что, если Derived было объявлено в другой библиотеке)?

Ответ на это на удивление прост: dynamic_cast может это знать, сохраняя это знание.

Когда компилятор генерирует код, он хранит данные об иерархиях классов в какой-то таблице, которые dynamic_cast может искать позже. Эта таблица может быть прикреплена к указателю vtable для удобного поиска с помощью реализации dynamic_cast. Данные, загруженные для typeid для этих классов, также могут храниться вместе с ними.

Если библиотеки задействованы, такого рода вещи обычно требуют, чтобы эти информационные структуры типа отображались в библиотеках, как и функции. Возможно, например, получить ошибку компоновщика, которая выглядит как "Undefined ссылка на" vtable для XXX "(и мальчик, это раздражает!), Опять же, как и с функциями.

Ответ 2

Магия.

Просто шучу. Если вы действительно хотите подробно изучить это, код, который реализует его для GCC, находится в libsupС++, части libstdС++.

https://github.com/mirrors/gcc/tree/master/libstdc%2B%2B-v3/libsupc%2B%2B

В частности, найдите все файлы с tinfo или type_info в их имени.

Или прочитайте описание здесь, что, вероятно, намного более доступно:

https://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti

Здесь подробно описывается формат информации о типе, которую генерирует компилятор, и должен дать вам понять, как поддержка времени выполнения находит правильный путь каста.

Ответ 3

Как может dynamic_cast узнать, был ли Derived2 получен из Derived (что, если Derived было объявлено в другой библиотеке)?

Сам dynamic_cast ничего не знает, его компилятор знает эти факты. Vtable не обязательно содержит только указатели на виртуальные функции.

Вот как бы я это сделал (наивно): my vtable будет содержать указатель к некоторой информации о типе (RTTI), используемой dynamic_cast. RTTI для типа будет содержать указатели на базовые классы, поэтому я могу подняться по иерархии классов. Псевдокод для роли будет выглядеть следующим образом:

Base* base = new Derived2; //base->vptr[RTTI_index] points to RTTI_of(Derived2)

//dynamic_cast<Derived*>(base):
RTTI* pRTTI = base->vptr[RTTI_index];
while (pRTTI && *pRTTI != RTTI_of(Derived))
{
  pRTTI = pRTTI->ParentRTTI;
}
if (pRTTI) return (Derived*)(base);
return NULL;