static_cast
is for situations when you know the cast can be done (either you cast to a parent class, or you have other ways of assessing the type of the class). There is no runtime check on the type (hence the static
). On the other hand, dynamic_cast
will check, at runtime, if the object is really of the type you want to cast it to. As for reinterpret_cast
, it doesn't do anything but using the same memory for a different purpose. Note that reinterpret_cast
should never be used to change from one class to another.
In the end, the reason static_cast
on NULL pointer crashes, is because a static_cast
with inheritance might requires a bit of pointer arithmetic form the compiler. This depend on how the compiler actually implement inheritance. But in case of multiple inheritance, it doesn't have a choice.
One way to see this is that the daughter class "contains" the parent class. It virtual table contains the one of the parent, but with added features. If the features are added at the beginning, then any cast to the parent class will point to a different place ... from where the features of the daughter class cannot be seen. I hope this make sense.
Note on pointer arithmetic
First, this is always be the case for multiple inheritance, but a compiler might choose to do so for single inheritance too.
Basically, if you look at the memory layout for an object content with virtual methods, you could do something like:
+---------------+----------------+
| ptr to vtable | members .... |
+---------------+----------------+
In case of single inheritance, this is pretty much enough. In particular, you can ensure that the vtable of any derived class starts with the vtable of the mother class and the first members are those of the mother class.
Now, if you have multiple inheritance, things are more complex. In particular, you probably can't merge vtables and members in a consistent way (at least not in the general case). So, say you inherit from classes A, B and C, you will probably have something like:
A B C
+----------------------+-----------+-----------+----------+-----------+-----+
| local vtable/members | vtable A | members A | vtable B | members B | ... |
+----------------------+-----------+-----------+----------+-----------+-----+
Such that if you point on A, you will see the object as an object of type A
, plus the rest. But if you want to see the object as being of type B
, you need to point to the address B, etc. Note, this might not be exactly what the system does, but that's the git of it.