Difference in behavior while using dynamic_cast with reference and pointers
Yes, this is correct behaviour. The reason is that you can have a null pointer, but not a null reference - any reference has to be bound to an object.
So when dynamic_cast for a pointer type fails it returns a null pointer and the caller can check for that, but when it fails for a reference type it can't return a null reference, so an exception is the only reasonable way to signal a problem.
When to use dynamic_cast of reference?
Basically if our object is allowed to be one of different types, we can dynamic_cast
to a pointer so we can check if the cast succeeded:
void do_if_derived(Base& b) {
Derived* d = dynamic_cast<Derived*>(&b);
if (d) {
// do something
}
else {
// not a Derived, this is OK
}
}
but if our object has to be a single specific type, we can dynamic_cast
to a reference and let the cast throw if it happens to be wrong:
void this_better_be_a_derived(Base& b)
{
Derived& d = dynamic_cast<Derived&>(b);
// do stuff with d
// will throw if, e.g. b is a DifferentDerived& instead
}
It's a matter of wanting to handle the failure case via a branch or via an exception.
Why is dynamic_cast'ing between pointers from different type hierarchies well defined?
The cast from C*
to D*
cannot be rejected by the compiler because dynamic_cast
can cast not only "up" and "down" a class hierarchy, but also "sideways". For example, suppose we have
struct E : C, D { };
C* p = new E;
auto q = dynamic_cast<D*>(p);
Then q
will point to the D
subobject of the complete E
object containing the C
object that p
points to.
This is specified in [expr.dynamic.cast]/(8.2).
Of course, a sufficiently smart compiler could still warn you, in some cases, that a dynamic_cast
is guaranteed to fail (if it knows where the pointer comes from).
dynamic cast for references
dynamic_cast
throws an exception on failure if used with references. To handle failure, catch the exception:
try {
XYZ& xyz = dynamic_cast<XYZ&>(abc);
}
catch (std::bad_cast& e) {
//handle error
}
Does dynamic_cast work even when there is no inheritance?
Sideways cast would be for example like this:
class Base1 { virtual ~Base1() = default; };
class Base2 { virtual ~Base2() = default; };
class Derived: public Base1, public Base2 {};
int main() {
Base1* p1 = new Derived;
Base2* p2 = dynamic_cast<Base2*>(p1);
}
Types Base1
and Base2
are unrelated to each other, but you can cast between the pointers because Derived
inherits from both.
I'm not sure what did the original answerer mean by "cast [...] up another chain", but I guess they meant a situation where you would have a Base1*
pointer and would cast to Base2
parent.
Regular cast vs. static_cast vs. dynamic_cast
static_cast
static_cast
is used for cases where you basically want to reverse an implicit conversion, with a few restrictions and additions. static_cast
performs no runtime checks. This should be used if you know that you refer to an object of a specific type, and thus a check would be unnecessary. Example:
void func(void *data) {
// Conversion from MyClass* -> void* is implicit
MyClass *c = static_cast<MyClass*>(data);
...
}
int main() {
MyClass c;
start_thread(&func, &c) // func(&c) will be called
.join();
}
In this example, you know that you passed a MyClass
object, and thus there isn't any need for a runtime check to ensure this.
dynamic_cast
dynamic_cast
is useful when you don't know what the dynamic type of the object is. It returns a null pointer if the object referred to doesn't contain the type casted to as a base class (when you cast to a reference, a bad_cast
exception is thrown in that case).
if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
...
}
You can not use dynamic_cast
for downcast (casting to a derived class) if the argument type is not polymorphic. For example, the following code is not valid, because Base
doesn't contain any virtual function:
struct Base { };
struct Derived : Base { };
int main() {
Derived d; Base *b = &d;
dynamic_cast<Derived*>(b); // Invalid
}
An "up-cast" (cast to the base class) is always valid with both static_cast
and dynamic_cast
, and also without any cast, as an "up-cast" is an implicit conversion (assuming the base class is accessible, i.e. it's a public
inheritance).
Regular Cast
These casts are also called C-style cast. A C-style cast is basically identical to trying out a range of sequences of C++ casts, and taking the first C++ cast that works, without ever considering dynamic_cast
. Needless to say, this is much more powerful as it combines all of const_cast
, static_cast
and reinterpret_cast
, but it's also unsafe, because it does not use dynamic_cast
.
In addition, C-style casts not only allow you to do this, but they also allow you to safely cast to a private base-class, while the "equivalent" static_cast
sequence would give you a compile-time error for that.
Some people prefer C-style casts because of their brevity. I use them for numeric casts only, and use the appropriate C++ casts when user defined types are involved, as they provide stricter checking.
dynamic_cast against random pointers?
From this dynamic_cast
reference:
dynamic_cast < new_type > ( expression )
...
expression
- lvalue of a complete class type ifnew_type
is a reference, prvalue of a pointer to complete class type ifnew_type
is a pointer.
[Emphasis mine]
The complete class type is important here, as it means you can't really pass any generic pointer to dynamic_cast
.
The type of expression
must also be related to the new_type
(i.e. a base-class, a child-class, or a sibling-class) or the behavior will be undefined.
If you use dynamic_cast
with any "random pointer" you will have undefined behavior, and while a compiler might be able to warn you about it (though not always possible) still attempting to do something leading to UB is on you as the programmer.
Related Topics
Convert a Char* to Std::String
Will an 'Empty' Constructor or Destructor Do the Same Thing as the Generated One
Undefined Reference to Template Function
Netbeans 7.2 Shows "Unable to Resolve Identifier" , Although Build Is Successful
Can a Cast Operator Be Explicit
Deprecated Conversion from String Literal to 'Char*'
Convert C++ Function Pointer to C Function Pointer
What Is an Opaque Value in C++
Disambiguate Overloaded Member Function Pointer Being Passed as Template Parameter
Should We Generally Use Float Literals for Floats Instead of the Simpler Double Literals
Qt/C++ - Accessing Mainwindow UI from a Different Class
Effective Way to Select Last Parameter of Variadic Template
How to Make Std::Make_Unique a Friend of My Class