c ++ manipulação de erro dynamic_cast
-
05-07-2019 - |
Pergunta
Existe alguma boas práticas relacionadas com o tratamento de erros dynamic_cast (não exceto a usá-lo quando você não tem que)? Eu estou querendo saber como eu deveria ir sobre NULL e bad_cast ele pode lançar. Devo verificar para ambos? E se eu pegar bad_cast ou detectar NULL eu provavelmente não pode recuperar de qualquer maneira ... Por agora, eu estou usando assert para verificar se dynamic_cast não retornou valor NULL. Você aceitaria essa solução em uma revisão de código?
Solução
Se o dynamic_cast
deve ter sucesso, seria uma boa prática para uso boost::polymorphic_downcast
vez, que vai um pouco algo como isto:
assert(dynamic_cast<T*>(o) == static_cast<T*>(o));
return static_cast<T*>(o);
Desta forma, você irá detectar erros na compilação de depuração e, ao mesmo tempo que evita a sobrecarga de tempo de execução em uma compilação de lançamento.
Se você suspeitar que o elenco pode falhar e você quiser detectá-lo, use dynamic_cast
e elenco para um tipo de referência. Este elenco vai jogar bad_cast
em caso de erro, e derrubar seu programa. (Isso é bom se, como você diz, você não está indo para recuperar de qualquer maneira)
T& t = dynamic_cast<T&>(o);
t.func(); //< Use t here, no extra check required
Use dynamic_cast
a um tipo de ponteiro apenas se o 0-ponteiro faz sentido no contexto. Você pode querer usá-lo em uma if
assim:
if (T* t = dynamic_cast<T*>(o)) {
t->func(); //< Use t here, it is valid
}
// consider having an else-clause
Com esta última opção que você precisa ter certeza de que o caminho de execução faz sentido se as dynamic_cast
retorna 0.
Para responder à sua pergunta diretamente: Eu preferiria uma das duas primeiras alternativas que dei a ter um assert
explícita no código:)
Outras dicas
bad_cast só é acionada quando lançando referências
dynamic_cast< Derived & >(baseclass)
NULL é devolvido quando casting ponteiros
dynamic_cast< Derived * >(&baseclass)
Assim, nunca há uma necessidade de verificar ambos.
Assert pode ser aceitável, mas isso depende muito do contexto, então, novamente, isso é verdade para praticamente todos os assert ...
Sim e não.
boost::polymorphic_downcast<>
é certamente uma boa opção para lidar com erros de dynamic_cast<>
durante a fase de depuração. No entanto vale a pena mencionar que polymorphic_downcast<>
deve ser utilizado quando é possível prever o tipo polimórfico passado em tempo de compilação , caso contrário o dynamic_cast<>
deve ser usado no lugar do mesmo.
No entanto, uma sequência de:
if (T1* t1 = dynamic_cast<T1*>(o))
{ }
if (T2* t2 = dynamic_cast<T2*>(o))
{ }
if (T3* t3 = dynamic_cast<T3*>(o))
{ }
denota um design muito ruim que deve ser resolver, por polimorfismo e funções virtuais .
Depende ...; -)
Se eu realmente esperava que o dynamic_cast
para me dar algo útil, por exemplo, se eu e mais ninguém adicionou um tipo polimórfico a um contêiner de ponteiros para uma classe base, então eu iria com o elenco de referência e deixe a matança std::bad_cast
minha aplicação -. não haveria muito o que fazer, realmente
No entanto, se eu estou consultando um tipo polimórfico para alguma capacidade exposta por uma interface que não tem necessariamente de implementar, em seguida, eu iria com o elenco ponteiro e, em seguida, um NULL não seria um erro ( a não ser, é claro, eu esperava a capacidade de realmente estar lá - mas então eu tinha ido para o elenco de referência, em primeiro lugar ...)
Eu concordo com a resposta 'depende', e também adicionar "degradação graciosa": só porque uma conversão falhar em algum lugar não é motivo suficiente para permitir que o aplicativo falhar (e que o usuário perder a sua / o seu trabalho, etc .). Eu recomendo uma combinação de afirma e programação defensiva:
ptr = dynamic_cast<MyClass>(obj);
ASSERT(ptr);
if(ptr)
{
// do stuff
}