Как обычно реализован dynamic_cast?
Является ли проверка типа простым целочисленным сравнением? Или имеет смысл иметь GetTypeId
виртуальная функция для различения, которая сделает это целочисленным сравнением?
(Просто не хочу, чтобы вещи сравнивались в именах классов)
РЕДАКТИРОВАТЬ: Я имею в виду, что если я часто ожидаю неправильный тип, имеет ли смысл использовать что-то вроде:
struct Token
{
enum {
AND,
OR,
IF
};
virtual std::size_t GetTokenId() = 0;
};
struct AndToken : public Token
{
std::size_t GetTokenId() { return AND; }
};
И использовать GetTokenId
член вместо того, чтобы полагаться на dynamic_cast
,
4 ответа
Функциональность dynamic_cast
выходит далеко за рамки простой проверки типов. Если бы это была просто проверка типов, ее было бы очень легко реализовать (что-то вроде того, что вы имели в своем первоначальном посте).
В дополнение к проверке типа, dynamic_cast
может выполнять приведение к void *
и иерархические перекрестные броски. Эти типы приведений концептуально требуют некоторой способности проходить иерархию классов в обоих направлениях (вверх и вниз). Структуры данных, необходимые для поддержки таких приведений, являются более сложными, чем простой скалярный идентификатор типа. Информация о dynamic_cast
использует является частью RTTI.
Попытка описать это здесь будет контрпродуктивной. Раньше у меня была хорошая ссылка, описывающая одну из возможных реализаций RTTI... постараюсь ее найти.
В некоторых оригинальных компиляторах вы правы, они использовали сравнение строк.
В результате dynamic_cast<> был очень медленным (условно говоря), так как иерархия классов обходилась, каждый шаг вверх / вниз по цепочке иерархии требовал сравнения строк с именем класса.
Это приводит к тому, что многие люди разрабатывают свои собственные методы литья. Это почти всегда было в конечном итоге бесполезно, так как требовалось, чтобы каждый класс был правильно аннотирован, а когда что-то пошло не так, было почти невозможно отследить ошибку.
Но это тоже древняя история.
Я не уверен, как это делается сейчас, но это определенно не предполагает сравнения строк. Делать это самостоятельно - тоже плохая идея (никогда не делайте работу, которую уже выполняет компилятор). Любая ваша попытка не будет такой же быстрой и точной, как у компилятора, помните, что годы разработки были направлены на то, чтобы сделать код компилятора настолько быстрым, насколько это возможно (и это всегда будет правильно).
Я не знаю точную реализацию, но вот идея, как бы я это сделал:
Кастинг от Derived*
в Base*
может быть сделано во время компиляции. Приведение между двумя не связанными полиморфными типами может быть выполнено и во время компиляции (просто верните NULL).
Кастинг от Base*
в Derived*
должно быть сделано во время выполнения, потому что возможно несколько производных классов. Идентификация динамического типа может быть выполнена с использованием таблицы виртуальных методов, привязанной к объекту (поэтому для этого требуются полиморфные классы).
Этот VMT, вероятно, содержит дополнительную информацию о базовых классах и их смещениях данных. Эти смещения данных актуальны, когда задействовано множественное наследование, и они добавляются к указателю источника, чтобы он указывал на правильное местоположение.
Если требуемый тип не был найден среди базовых классов, dynamic_cast
вернул бы ноль.
Компилятор не может угадать дополнительную информацию, которую вы можете иметь, и вставить ее в dynamic_cast. Если вы знаете определенные инварианты о вашем коде и можете показать, что ваш ручной механизм приведения быстрее, сделайте это сами. На самом деле не имеет значения, как dynamic_cast реализован в этом случае.