324просмотров
15 октября 2025 г.
Score: 356
Многим известно как работает dynamic_cast в C++. Зная, что за указателем на базовый класс стоит объект производного, мы можем скастовать базовый до производного и вызвать его метод. Пример:
struct Base { virtual ~Base() = default;
};
struct Derived : public Base { void doDerivedJob() { std::cout << "derived" << std::endl; }
}; void cast(Base baseP) { Derived d = dynamic_cast<Derived>(baseP); if (d) { d->doDerivedJob(); } else { std::cout << "ptr: unable to cast" << std::endl; }
} int main(int argc, char argv<::>)
{ Base b; Derived d; cast(&b); // ptr: unable to cast cast(&d); // derived return 0;
}
Все, что нужно для корректной работы dynamic_cast - это наличие хотя бы одного виртуального метода. И тогда мы можем в рантайме проверить результат кастования на nullptr. Если это nullptr - то мы не угадали и указатель смотрел на объект базового класса.
Обычно такой подход рассматривается как результат неудачного проектирования, но иногда все же приходится так делать. К тому же проверка указателя на nullptr довольно обычная операция. Но что делать, если мы имеем не указатель, а ссылку на базовый класс? Оказывается, что dynamic_cast работает как ожидается и со ссылками - можно преобразовать ссылку на базовый класс в ссылку на производный. Однако, что делать, если преобразование в рантайме невозможно? Ссылка, в отличие от указателя не может указывать на nullptr. В случае ошибки преобразования dynamic_cast выбросит исключение std::bad_cast. Пример:
void cast(Base & baseRef) { try { Derived& d = dynamic_cast<Derived&>(baseRef); d.doDerivedJob(); } catch (const std::bad_cast& ex) { std::cout << "ref: unable to cast: " << ex.what() << std::endl; // baseRef.doWork(); }
} int main(int argc, char* argv<::>)
{ Base b; Derived d; cast(b); // ref: unable to cast: Bad dynamic_cast cast(d); // derived return 0;
}
Однако, этот пример показывает плохой стиль программирования, потому что использует исключения для потока выполнения.