Is there any good practice related to dynamic_cast error handling (except not using it when you don't have to)? I'm wondering how should I go about NULL and bad_cast it can throw. Should I check for both? And if I catch bad_cast or detect NULL I probably can't recover anyway... For now, I'm using assert to check if dynamic_cast returned not NULL value. Would you accept this solution on a code review?
If a dynamic_cast fails, the result of the conversion will be a null pointer. Because we haven't checked for a null pointer result, we access d->getName(), which will try to dereference a null pointer, leading to undefined behavior (probably a crash).
dynamic_cast will no longer throw an exception when type-id is an interior pointer to a value type, with the cast failing at runtime. The cast will now return the 0 pointer value instead of throwing.
Yes, dynamic_cast is a code smell, but so is adding functions that try to make it look like you have a good polymorphic interface but are actually equal to a dynamic_cast i.e. stuff like can_put_on_board .
While typeid + static_cast is faster than dynamic_cast , not having to switch on the runtime type of the object is faster than any of them.
If the dynamic_cast
should succeed, it would be good practice to use boost::polymorphic_downcast
instead, which goes a little something like this:
assert(dynamic_cast<T*>(o) == static_cast<T*>(o)); return static_cast<T*>(o);
This way, you will detect errors in the debug build while at the same time avoiding the runtime overhead in a release build.
If you suspect the cast might fail and you want to detect it, use dynamic_cast
and cast to a reference type. This cast will throw bad_cast
in case of error, and will take down your program. (This is good if, as you say, you are not going to recover anyway)
T& t = dynamic_cast<T&>(o); t.func(); //< Use t here, no extra check required
Use dynamic_cast
to a pointer type only if the 0-pointer makes sense in the context. You might want to use it in an if
like this:
if (T* t = dynamic_cast<T*>(o)) { t->func(); //< Use t here, it is valid } // consider having an else-clause
With this last option you need to make sure that the execution path makes sense if the dynamic_cast
returns 0.
To answer your question directly: I would prefer one of the two first alternatives I have given to having an explicit assert
in the code :)
bad_cast is only thrown when casting references
dynamic_cast< Derived & >(baseclass)
NULL is returned when casting pointers
dynamic_cast< Derived * >(&baseclass)
So there's never a need to check both.
Assert can be acceptable, but that greatly depends on the context, then again, that's true for pretty much every assert...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With