gestione degli errori c++ Dynamic_cast
-
05-07-2019 - |
Domanda
Esistono buone pratiche relative alla gestione degli errori di Dynamic_cast (tranne non utilizzarlo quando non è necessario)?Mi chiedo come dovrei procedere con NULL e bad_cast che può lanciare.Dovrei controllare entrambi?E se rilevo bad_cast o rilevo NULL probabilmente non riesco comunque a recuperare...Per ora, sto utilizzando assert per verificare se Dynamic_cast ha restituito un valore non NULL.Accetteresti questa soluzione durante una revisione del codice?
Soluzione
Se il dynamic_cast
dovrebbe avere successo, sarebbe buona norma usare boost::polymorphic_downcast
invece, che va un po 'come questo:
assert(dynamic_cast<T*>(o) == static_cast<T*>(o));
return static_cast<T*>(o);
In questo modo, verranno rilevati errori nella build di debug evitando allo stesso tempo il sovraccarico del runtime in una build di rilascio.
Se sospetti che il cast potrebbe fallire e vuoi rilevarlo, usa bad_cast
e lancia un tipo di riferimento. Questo cast genererà if
in caso di errore e eliminerà il programma. (Questo va bene se, come dici tu, non hai intenzione di recuperare comunque)
T& t = dynamic_cast<T&>(o);
t.func(); //< Use t here, no extra check required
Usa assert
per un tipo di puntatore solo se il puntatore 0 ha senso nel contesto. Potresti volerlo usare in <=> come questo:
if (T* t = dynamic_cast<T*>(o)) {
t->func(); //< Use t here, it is valid
}
// consider having an else-clause
Con quest'ultima opzione è necessario assicurarsi che il percorso di esecuzione abbia senso se <=> restituisce 0.
Per rispondere direttamente alla tua domanda: preferirei una delle due prime alternative che ho dato ad avere un esplicito <=> nel codice :)
Altri suggerimenti
bad_cast viene lanciato solo quando si trasmettono riferimenti
dynamic_cast< Derived & >(baseclass)
NULL viene restituito quando si lanciano i puntatori
dynamic_cast< Derived * >(&baseclass)
Quindi non è mai necessario controllare entrambi.
L'asserzione può essere accettabile, ma ciò dipende molto dal contesto, quindi, questo è vero per praticamente ogni asserzione ...
Sì e no.
boost::polymorphic_downcast<>
è sicuramente una buona opzione per gestire gli errori di dynamic_cast<>
durante la fase di debug.Tuttavia vale la pena menzionarlo polymorphic_downcast<>
dovrebbe essere usato solo quando è possibile prevedere il tipo polimorfico passato in fase di compilazione, altrimenti il dynamic_cast<>
dovrebbe essere usato al suo posto.
Tuttavia una sequenza di:
if (T1* t1 = dynamic_cast<T1*>(o))
{ }
if (T2* t2 = dynamic_cast<T2*>(o))
{ }
if (T3* t3 = dynamic_cast<T3*>(o))
{ }
denota un design pessimo che dovrebbe essere risolto polimorfismo E funzioni virtuali.
Dipende ... ;-)
Se davvero mi aspettassi che dynamic_cast
mi dia qualcosa di utilizzabile, ad esempio se io e nessun altro aggiungessimo un tipo polimorfico a un contenitore di puntatori a una classe base, allora andrei con il cast di riferimento e lascerei che il std::bad_cast
uccidi la mia applicazione - non ci sarebbe molto altro da fare, davvero.
Tuttavia, se sto interrogando un tipo polimorfico per alcune funzionalità esposte da un'interfaccia che non deve necessariamente implementare, allora andrei con il cast del puntatore e quindi un NULL non sarebbe un errore ( a meno che, ovviamente, non mi aspettassi la possibilità di davvero essere lì - ma poi ero andato per il cast di riferimento in primo luogo ...)
Concordo con la risposta 'dipende', e aggiungo anche " Degrado grazioso " ;: solo perché un cast fallisce da qualche parte non è motivo sufficiente per far fallire l'applicazione (e il l'utente perde il proprio lavoro, ecc.). Consiglierei una combinazione di asserzioni e programmazione difensiva:
ptr = dynamic_cast<MyClass>(obj);
ASSERT(ptr);
if(ptr)
{
// do stuff
}