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
}
Другие вопросы по тегам