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?

È stato utile?

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
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top