C++ dynamic_cast обработка ошибок
Есть ли полезная практика, связанная с обработкой ошибок dynamic_cast (кроме случаев, когда вы не используете ее, когда это не нужно)? Мне интересно, как я должен идти о NULL и bad_cast, который он может выбросить. Должен ли я проверить оба? И если я ловлю bad_cast или обнаруживаю NULL, я, вероятно, все равно не смогу восстановиться... Сейчас я использую assert, чтобы проверить, вернул ли dynamic_cast значение NULL. Примете ли вы это решение в обзоре кода?
5 ответов
Если dynamic_cast
должно быть успешным, было бы полезно использовать boost::polymorphic_downcast
вместо этого, который выглядит примерно так:
assert(dynamic_cast<T*>(o) == static_cast<T*>(o));
return static_cast<T*>(o);
Таким образом, вы будете обнаруживать ошибки в отладочной сборке, в то же время избегая накладных расходов во время сборки выпуска.
Если вы подозреваете, что приведение может быть неудачным, и вы хотите его обнаружить, используйте dynamic_cast
и приведение к ссылочному типу. Этот бросок бросит bad_cast
в случае ошибки, и снимет вашу программу. (Это хорошо, если, как вы говорите, вы все равно не восстановитесь)
T& t = dynamic_cast<T&>(o);
t.func(); //< Use t here, no extra check required
использование dynamic_cast
к типу указателя, только если 0-указатель имеет смысл в контексте. Вы можете использовать его в if
как это:
if (T* t = dynamic_cast<T*>(o)) {
t->func(); //< Use t here, it is valid
}
// consider having an else-clause
С этим последним вариантом вам нужно убедиться, что путь выполнения имеет смысл, если dynamic_cast
возвращает 0
Чтобы ответить на ваш вопрос напрямую: я бы предпочел одну из двух первых альтернатив, которые я дал, чтобы иметь явный assert
в коде:)
bad_cast генерируется только при касте ссылок
dynamic_cast< Derived & >(baseclass)
NULL возвращается при наведении указателей
dynamic_cast< Derived * >(&baseclass)
Так что никогда не нужно проверять оба.
Утверждение может быть приемлемым, но это в значительной степени зависит от контекста, и опять же, это верно практически для каждого утверждения...
И да и нет.
boost::polymorphic_downcast<>
это, безусловно, хороший вариант для обработки ошибок dynamic_cast<>
на этапе отладки. Однако стоит отметить, что polymorphic_downcast<>
следует использовать только тогда, когда можно предсказать полиморфный тип, переданный во время компиляции, в противном случае dynamic_cast<>
должно быть использовано вместо этого.
Однако последовательность:
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
}