c ++ dynamic_cast обработка ошибок
-
05-07-2019 - |
Вопрос
Есть ли полезная практика, связанная с обработкой ошибок dynamic_cast (кроме случаев, когда вы не используете ее, когда это не нужно)? Мне интересно, как я должен идти о NULL и bad_cast, который он может выбросить. Должен ли я проверить оба? И если я поймаю bad_cast или обнаружу NULL, я, вероятно, все равно не смогу восстановиться ... На данный момент я использую assert, чтобы проверить, вернул ли dynamic_cast значение NULL. Примете ли вы это решение в обзоре кода?
Решение
Если dynamic_cast
должен выполнить, было бы неплохо использовать boost::polymorphic_downcast
вместо этого, что выглядит примерно так:
assert(dynamic_cast<T*>(o) == static_cast<T*>(o));
return static_cast<T*>(o);
Таким образом, вы будете обнаруживать ошибки в отладочной сборке, в то же время избегая накладных расходов времени выполнения в сборке выпуска.
Если вы подозреваете, что приведение может завершиться неудачно, и вы хотите его обнаружить, используйте bad_cast
и приведите к ссылочному типу. Это приведение будет вызывать if
в случае ошибки и завершит работу вашей программы. (Это хорошо, если, как вы говорите, вы все равно не восстановитесь)
T& t = dynamic_cast<T&>(o);
t.func(); //< Use t here, no extra check required
Используйте assert
для типа указателя, только если 0-указатель имеет смысл в контексте. Возможно, вы захотите использовать его в <=> таком виде:
if (T* t = dynamic_cast<T*>(o)) {
t->func(); //< Use t here, it is valid
}
// consider having an else-clause
С помощью этой последней опции вам нужно убедиться, что путь выполнения имеет смысл, если <=> возвращает 0.
Чтобы ответить на ваш вопрос напрямую: я бы предпочел одну из двух первых альтернатив, которые я дал, чтобы в коде было явное <=>:)
Другие советы
bad_cast генерируется только при приведении ссылок
dynamic_cast< Derived & >(baseclass)
NULL возвращается при приведении указателей
dynamic_cast< Derived * >(&baseclass)
Так что никогда не нужно проверять оба.
Утверждение может быть приемлемым, но это в значительной степени зависит от контекста, и опять же, это верно практически для каждого утверждения ...
Да и нет.
boost::polymorphic_downcast<>
, безусловно, является хорошим вариантом для обработки ошибок dynamic_cast<>
на этапе отладки. Однако стоит отметить, что polymorphic_downcast<>
следует использовать только тогда, когда можно предсказать полиморфный тип, переданный во время компиляции , в противном случае вместо него следует использовать <=>.
Однако последовательность:
if (T1* t1 = dynamic_cast<T1*>(o))
{ }
if (T2* t2 = dynamic_cast<T2*>(o))
{ }
if (T3* t3 = dynamic_cast<T3*>(o))
{ }
обозначает очень плохой дизайн, который должен быть решен с помощью полиморфизма и виртуальных функций .
Это зависит ...; -)
Если бы я действительно ожидал, что dynamic_cast
даст мне что-то пригодное для использования, например, если я и никто другой не добавил полиморфный тип в контейнер указателей на базовый класс, то я бы пошел с приведением ссылки и позволил std::bad_cast
убить мое приложение - больше ничего не поделаешь.
Однако, если я запрашиваю полиморфный тип для некоторой возможности, предоставляемой интерфейсом, который он не обязательно должен реализовывать, то я бы пошел с приведением указателя, и тогда NULL не был бы ошибкой ( если, конечно, я не ожидал, что возможность действительно будет присутствовать, - но тогда я пошел бы за эталонный состав в первую очередь ...)
Я бы согласился с ответом «все зависит», а также добавил бы «! пользователь теряет свою работу и т. д.). Я бы порекомендовал комбинацию утверждений и защитного программирования:
ptr = dynamic_cast<MyClass>(obj);
ASSERT(ptr);
if(ptr)
{
// do stuff
}