Что будет при динамическом кастовании указателя в C++
Что проверяют
Знаете ли вы, чем dynamic_cast отличается от остальных приведений, понимаете ли его цену и требования к типу. Заодно смотрят на отношение к дизайну: частый dynamic_cast в коде это обычно признак того, что в базовом классе не хватает виртуального метода.
Как отвечать
dynamic_cast проверяет фактический тип объекта во время выполнения по данным RTTI. Требование одно, но жёсткое: исходный тип должен быть полиморфным, то есть иметь хотя бы одну виртуальную функцию, обычно виртуальный деструктор. Дальше важна форма приведения:
- для указателей при неудаче возвращается
nullptr, результат обязан проверяться; - для ссылок нулевого значения не существует, поэтому бросается
std::bad_cast; - приведение вверх по иерархии разрешается статически и никакой проверки не стоит;
- умеет то, чего не умеет
static_cast: перекрёстное приведение между ветками множественного наследования, спуск через виртуальное наследование и приведение кvoid*, дающее адрес начала полного объекта.
struct Base { virtual ~Base() = default; };
struct Derived : Base { void f(); };
Base* p = new Derived;
if (auto* d = dynamic_cast<Derived*>(p)) d->f();
Base b;
Derived& r = dynamic_cast<Derived&>(b); // std::bad_castПро стоимость честно скажите, что это не константа: проверка обходит иерархию и сравнивает записи типов, поэтому в горячем цикле её не держат. Компилятор умеет отключать RTTI целиком, и тогда dynamic_cast вниз просто перестанет собираться.
Где спотыкаются
Забывают виртуальный деструктор. Класс не полиморфный, и код не компилируется с жалобой на то, что исходный тип не полиморфен. Ошибку читают как проблему шаблонов, хотя лечится она одной строкой в базовом классе.
Второе: не проверяют результат и сразу разыменовывают указатель. Ради этой проверки dynamic_cast и существует, иначе дешевле и честнее static_cast.
Третье: ждут nullptr от ссылочной версии и оборачивают её в проверку на ноль. Ссылка не бывает пустой, и неудачное приведение улетит исключением мимо такой проверки.