static_cast
vs dynamic_cast
vs reinterpret_cast
internals view on a downcast/upcast
In this answer, I want to compare these three mechanisms on a concrete upcast/downcast example and analyze what happens to the underlying pointers/memory/assembly to give a concrete understanding of how they compare.
I believe that this will give a good intuition on how those casts are different:
static_cast
: does one address offset at runtime (low runtime impact) and no safety checks that a downcast is correct.
dyanamic_cast
: does the same address offset at runtime like static_cast
, but also and an expensive safety check that a downcast is correct using RTTI.
This safety check allows you to query if a base class pointer is of a given type at runtime by checking a return of nullptr
which indicates an invalid downcast.
Therefore, if your code is not able to check for that nullptr
and take a valid non-abort action, you should just use static_cast
instead of dynamic cast.
If an abort is the only action your code can take, maybe you only want to enable the dynamic_cast
in debug builds (-NDEBUG
), and use static_cast
otherwise, e.g. as done here, to not slow down your fast runs.
reinterpret_cast
: does nothing at runtime, not even the address offset. The pointer must point exactly to the correct type, not even a base class works. You generally don't want this unless raw byte streams are involved.
Consider the following code example:
main.cpp
#include <iostream>
struct B1 {
B1(int int_in_b1) : int_in_b1(int_in_b1) {}
virtual ~B1() {}
void f0() {}
virtual int f1() { return 1; }
int int_in_b1;
};
struct B2 {
B2(int int_in_b2) : int_in_b2(int_in_b2) {}
virtual ~B2() {}
virtual int f2() { return 2; }
int int_in_b2;
};
struct D : public B1, public B2 {
D(int int_in_b1, int int_in_b2, int int_in_d)
: B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
void d() {}
int f2() { return 3; }
int int_in_d;
};
int main() {
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1, 2, 3};
// The memory layout must support the virtual method call use case.
b2s[0] = &b2;
// An upcast is an implicit static_cast<>().
b2s[1] = &d;
std::cout << "&d " << &d << std::endl;
std::cout << "b2s[0] " << b2s[0] << std::endl;
std::cout << "b2s[1] " << b2s[1] << std::endl;
std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;
// Now for some downcasts.
// Cannot be done implicitly
// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
// dp = (b2s[0]);
// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp = static_cast<D*>(b2s[0]);
std::cout << "static_cast<D*>(b2s[0]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = static_cast<D*>(b2s[1]);
std::cout << "static_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Segfault because dp is nullptr.
dp = dynamic_cast<D*>(b2s[0]);
std::cout << "dynamic_cast<D*>(b2s[0]) " << dp << std::endl;
//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = dynamic_cast<D*>(b2s[1]);
std::cout << "dynamic_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Undefined behaviour to an unrelated memory address because this
// did not calculate the offset to get from B2* to D*.
dp = reinterpret_cast<D*>(b2s[1]);
std::cout << "reinterpret_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}
Compile, run and disassemble with:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out
where setarch
is used to disable ASLR to make it easier to compare runs.
Possible output:
&d 0x7fffffffc930
b2s[0] 0x7fffffffc920
b2s[1] 0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0]) 0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d 1
static_cast<D*>(b2s[1]) 0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d 3
dynamic_cast<D*>(b2s[0]) 0
dynamic_cast<D*>(b2s[1]) 0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767
Now, as mentioned at: https://en.wikipedia.org/wiki/Virtual_method_table in order to support the virtual method calls efficiently, the memory data structure of D
has to look something like:
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
D:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
The key fact is that the memory data structure of D
contains inside it memory structure compatible with that of B1
and that of B2
internally.
Therefore we reach the critical conclusion:
an upcast or downcast only needs to shift the pointer value by a value known at compile time
This way, when D
gets passed to the base type array, the type cast actually calculates that offset and points something that looks exactly like a valid B2
in memory:
b2s[1] = &d;
except that this one has the vtable for D
instead of B2
, and therefore all virtual calls work transparently.
Now, we can finally get back to type casting and the analysis of our concrete example.
From the stdout output we see:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
Therefore, the implicit static_cast
done there did correctly calculate the offset from the full D
data structure at 0x7fffffffc930 to the B2
like one which is at 0x7fffffffc940. We also infer that what lies between 0x7fffffffc930 and 0x7fffffffc940 is likely be the B1
data and vtable.
Then, on the downcast sections, it is now easy to understand how the invalid ones fail and why:
static_cast<D*>(b2s[0]) 0x7fffffffc910
: the compiler just went up 0x10 at compile time bytes to try and go from a B2
to the containing D
But because b2s[0]
was not a D
, it now points to an undefined memory region.
The disassembly is:
49 dp = static_cast<D*>(b2s[0]);
0x0000000000000fc8 <+414>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fcc <+418>: 48 85 c0 test %rax,%rax
0x0000000000000fcf <+421>: 74 0a je 0xfdb <main()+433>
0x0000000000000fd1 <+423>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fd5 <+427>: 48 83 e8 10 sub $0x10,%rax
0x0000000000000fd9 <+431>: eb 05 jmp 0xfe0 <main()+438>
0x0000000000000fdb <+433>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000000fe0 <+438>: 48 89 45 98 mov %rax,-0x68(%rbp)
so we see that GCC does:
D
which does not existdynamic_cast<D*>(b2s[0]) 0
: C++ actually found that the cast was invalid and returned nullptr
!
There is no way this can be done at compile time, and we will confirm that from the disassembly:
59 dp = dynamic_cast<D*>(b2s[0]);
0x00000000000010ec <+706>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x00000000000010f0 <+710>: 48 85 c0 test %rax,%rax
0x00000000000010f3 <+713>: 74 1d je 0x1112 <main()+744>
0x00000000000010f5 <+715>: b9 10 00 00 00 mov $0x10,%ecx
0x00000000000010fa <+720>: 48 8d 15 f7 0b 20 00 lea 0x200bf7(%rip),%rdx # 0x201cf8 <_ZTI1D>
0x0000000000001101 <+727>: 48 8d 35 28 0c 20 00 lea 0x200c28(%rip),%rsi # 0x201d30 <_ZTI2B2>
0x0000000000001108 <+734>: 48 89 c7 mov %rax,%rdi
0x000000000000110b <+737>: e8 c0 fb ff ff callq 0xcd0 <__dynamic_cast@plt>
0x0000000000001110 <+742>: eb 05 jmp 0x1117 <main()+749>
0x0000000000001112 <+744>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000001117 <+749>: 48 89 45 98 mov %rax,-0x68(%rbp)
First there is a NULL check, and it returns NULL if th einput is NULL.
Otherwise, it sets up some arguments in the RDX, RSI and RDI and calls __dynamic_cast
.
I don't have the patience to analyze this further now, but as others said, the only way for this to work is for __dynamic_cast
to access some extra RTTI in-memory data structures that represent the class hierarchy.
It must therefore start from the B2
entry for that table, then walk this class hierarchy until it finds that the vtable for a D
typecast from b2s[0]
.
This is why reinterpret cast is potentially expensive! Here is an example where a one liner patch converting a dynamic_cast
to a static_cast
in a complex project reduced runtime by 33%!.
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
this one just believes us blindly: we said there is a D
at address b2s[1]
, and the compiler does no offset calculations.
But this is wrong, because D is actually at 0x7fffffffc930, what is at 0x7fffffffc940 is the B2-like structure inside D! So trash gets accessed.
We can confirm this from the horrendous -O0
assembly that just moves the value around:
70 dp = reinterpret_cast<D*>(b2s[1]);
0x00000000000011fa <+976>: 48 8b 45 d8 mov -0x28(%rbp),%rax
0x00000000000011fe <+980>: 48 89 45 98 mov %rax,-0x68(%rbp)
Related questions:
Tested on Ubuntu 18.04 amd64, GCC 7.4.0.
Does this answer your question?
I have never used reinterpret_cast
, and wonder whether running into a case that needs it isn't a smell of bad design. In the code base I work on dynamic_cast
is used a lot. The difference with static_cast
is that a dynamic_cast
does runtime checking which may (safer) or may not (more overhead) be what you want (see msdn).
Use dynamic_cast
for converting pointers/references within an inheritance hierarchy.
Use static_cast
for ordinary type conversions.
Use reinterpret_cast
for low-level reinterpreting of bit patterns. Use with extreme caution.
Use const_cast
for casting away const/volatile
. Avoid this unless you are stuck using a const-incorrect API.
To understand, let's consider below code snippet:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Only line (4) compiles without error. Only reinterpret_cast can be used to convert a pointer to an object to a pointer to an any unrelated object type.
One this to be noted is: The dynamic_cast would fail at run-time, however on most compilers it will also fail to compile because there are no virtual functions in the struct of the pointer being casted, meaning dynamic_cast will work with only polymorphic class pointers.
When to use C++ cast:
Use dynamic_cast
for converting pointers/references within an inheritance hierarchy.
Use static_cast
for ordinary type conversions.
Use reinterpret_cast
for low-level reinterpreting of bit patterns. Use with extreme caution.
Use const_cast
for casting away const/volatile
. Avoid this unless you are stuck using a const-incorrect API.
It might help if you know little bit of internals...
static_cast
float
to int
. Use static_cast
for them.A
to B
, static_cast
calls B
's constructor passing A
as param. Alternatively, A
could have a conversion operator (i.e. A::operator B()
). If B
doesn't have such constructor, or A
doesn't have a conversion operator, then you get compile time error.A*
to B*
always succeeds if A and B are in inheritance hierarchy (or void) otherwise you get compile error.A&
to B&
.dynamic_cast
(Base*)
to (Derived*)
may fail if pointer is not actually of derived type.A*
to B*
, if cast is invalid then dynamic_cast will return nullptr.A&
to B&
if cast is invalid then dynamic_cast will throw bad_cast exception.const_cast
set<T>
which only returns its elements as const to make sure you don't change its key. However if your intent is to modify object's non-key members then it should be ok. You can use const_cast to remove constness.T& SomeClass::foo()
as well as const T& SomeClass::foo() const
. To avoid code duplication, you can apply const_cast to return value of one function from another.reinterpret_cast
float
to 4 bytes of int
to see how bits in float
looks like.In addition to the other answers so far, here is unobvious example where static_cast
is not sufficient so that reinterpret_cast
is needed. Suppose there is a function which in an output parameter returns pointers to objects of different classes (which do not share a common base class). A real example of such function is CoCreateInstance()
(see the last parameter, which is in fact void**
). Suppose you request particular class of object from this function, so you know in advance the type for the pointer (which you often do for COM objects). In this case you cannot cast pointer to your pointer into void**
with static_cast
: you need reinterpret_cast<void**>(&yourPointer)
.
In code:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
//static_cast<void**>(&pNetFwPolicy2) would give a compile error
reinterpret_cast<void**>(&pNetFwPolicy2) );
However, static_cast
works for simple pointers (not pointers to pointers), so the above code can be rewritten to avoid reinterpret_cast
(at a price of an extra variable) in the following way:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
&tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
It might help if you know little bit of internals...
static_cast
float
to int
. Use static_cast
for them.A
to B
, static_cast
calls B
's constructor passing A
as param. Alternatively, A
could have a conversion operator (i.e. A::operator B()
). If B
doesn't have such constructor, or A
doesn't have a conversion operator, then you get compile time error.A*
to B*
always succeeds if A and B are in inheritance hierarchy (or void) otherwise you get compile error.A&
to B&
.dynamic_cast
(Base*)
to (Derived*)
may fail if pointer is not actually of derived type.A*
to B*
, if cast is invalid then dynamic_cast will return nullptr.A&
to B&
if cast is invalid then dynamic_cast will throw bad_cast exception.const_cast
set<T>
which only returns its elements as const to make sure you don't change its key. However if your intent is to modify object's non-key members then it should be ok. You can use const_cast to remove constness.T& SomeClass::foo()
as well as const T& SomeClass::foo() const
. To avoid code duplication, you can apply const_cast to return value of one function from another.reinterpret_cast
float
to 4 bytes of int
to see how bits in float
looks like.To understand, let's consider below code snippet:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Only line (4) compiles without error. Only reinterpret_cast can be used to convert a pointer to an object to a pointer to an any unrelated object type.
One this to be noted is: The dynamic_cast would fail at run-time, however on most compilers it will also fail to compile because there are no virtual functions in the struct of the pointer being casted, meaning dynamic_cast will work with only polymorphic class pointers.
When to use C++ cast:
static_cast
vs dynamic_cast
vs reinterpret_cast
internals view on a downcast/upcast
In this answer, I want to compare these three mechanisms on a concrete upcast/downcast example and analyze what happens to the underlying pointers/memory/assembly to give a concrete understanding of how they compare.
I believe that this will give a good intuition on how those casts are different:
static_cast
: does one address offset at runtime (low runtime impact) and no safety checks that a downcast is correct.
dyanamic_cast
: does the same address offset at runtime like static_cast
, but also and an expensive safety check that a downcast is correct using RTTI.
This safety check allows you to query if a base class pointer is of a given type at runtime by checking a return of nullptr
which indicates an invalid downcast.
Therefore, if your code is not able to check for that nullptr
and take a valid non-abort action, you should just use static_cast
instead of dynamic cast.
If an abort is the only action your code can take, maybe you only want to enable the dynamic_cast
in debug builds (-NDEBUG
), and use static_cast
otherwise, e.g. as done here, to not slow down your fast runs.
reinterpret_cast
: does nothing at runtime, not even the address offset. The pointer must point exactly to the correct type, not even a base class works. You generally don't want this unless raw byte streams are involved.
Consider the following code example:
main.cpp
#include <iostream>
struct B1 {
B1(int int_in_b1) : int_in_b1(int_in_b1) {}
virtual ~B1() {}
void f0() {}
virtual int f1() { return 1; }
int int_in_b1;
};
struct B2 {
B2(int int_in_b2) : int_in_b2(int_in_b2) {}
virtual ~B2() {}
virtual int f2() { return 2; }
int int_in_b2;
};
struct D : public B1, public B2 {
D(int int_in_b1, int int_in_b2, int int_in_d)
: B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
void d() {}
int f2() { return 3; }
int int_in_d;
};
int main() {
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1, 2, 3};
// The memory layout must support the virtual method call use case.
b2s[0] = &b2;
// An upcast is an implicit static_cast<>().
b2s[1] = &d;
std::cout << "&d " << &d << std::endl;
std::cout << "b2s[0] " << b2s[0] << std::endl;
std::cout << "b2s[1] " << b2s[1] << std::endl;
std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;
// Now for some downcasts.
// Cannot be done implicitly
// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
// dp = (b2s[0]);
// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp = static_cast<D*>(b2s[0]);
std::cout << "static_cast<D*>(b2s[0]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = static_cast<D*>(b2s[1]);
std::cout << "static_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Segfault because dp is nullptr.
dp = dynamic_cast<D*>(b2s[0]);
std::cout << "dynamic_cast<D*>(b2s[0]) " << dp << std::endl;
//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = dynamic_cast<D*>(b2s[1]);
std::cout << "dynamic_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Undefined behaviour to an unrelated memory address because this
// did not calculate the offset to get from B2* to D*.
dp = reinterpret_cast<D*>(b2s[1]);
std::cout << "reinterpret_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}
Compile, run and disassemble with:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out
where setarch
is used to disable ASLR to make it easier to compare runs.
Possible output:
&d 0x7fffffffc930
b2s[0] 0x7fffffffc920
b2s[1] 0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0]) 0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d 1
static_cast<D*>(b2s[1]) 0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d 3
dynamic_cast<D*>(b2s[0]) 0
dynamic_cast<D*>(b2s[1]) 0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767
Now, as mentioned at: https://en.wikipedia.org/wiki/Virtual_method_table in order to support the virtual method calls efficiently, the memory data structure of D
has to look something like:
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
D:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
The key fact is that the memory data structure of D
contains inside it memory structure compatible with that of B1
and that of B2
internally.
Therefore we reach the critical conclusion:
an upcast or downcast only needs to shift the pointer value by a value known at compile time
This way, when D
gets passed to the base type array, the type cast actually calculates that offset and points something that looks exactly like a valid B2
in memory:
b2s[1] = &d;
except that this one has the vtable for D
instead of B2
, and therefore all virtual calls work transparently.
Now, we can finally get back to type casting and the analysis of our concrete example.
From the stdout output we see:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
Therefore, the implicit static_cast
done there did correctly calculate the offset from the full D
data structure at 0x7fffffffc930 to the B2
like one which is at 0x7fffffffc940. We also infer that what lies between 0x7fffffffc930 and 0x7fffffffc940 is likely be the B1
data and vtable.
Then, on the downcast sections, it is now easy to understand how the invalid ones fail and why:
static_cast<D*>(b2s[0]) 0x7fffffffc910
: the compiler just went up 0x10 at compile time bytes to try and go from a B2
to the containing D
But because b2s[0]
was not a D
, it now points to an undefined memory region.
The disassembly is:
49 dp = static_cast<D*>(b2s[0]);
0x0000000000000fc8 <+414>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fcc <+418>: 48 85 c0 test %rax,%rax
0x0000000000000fcf <+421>: 74 0a je 0xfdb <main()+433>
0x0000000000000fd1 <+423>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fd5 <+427>: 48 83 e8 10 sub $0x10,%rax
0x0000000000000fd9 <+431>: eb 05 jmp 0xfe0 <main()+438>
0x0000000000000fdb <+433>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000000fe0 <+438>: 48 89 45 98 mov %rax,-0x68(%rbp)
so we see that GCC does:
D
which does not existdynamic_cast<D*>(b2s[0]) 0
: C++ actually found that the cast was invalid and returned nullptr
!
There is no way this can be done at compile time, and we will confirm that from the disassembly:
59 dp = dynamic_cast<D*>(b2s[0]);
0x00000000000010ec <+706>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x00000000000010f0 <+710>: 48 85 c0 test %rax,%rax
0x00000000000010f3 <+713>: 74 1d je 0x1112 <main()+744>
0x00000000000010f5 <+715>: b9 10 00 00 00 mov $0x10,%ecx
0x00000000000010fa <+720>: 48 8d 15 f7 0b 20 00 lea 0x200bf7(%rip),%rdx # 0x201cf8 <_ZTI1D>
0x0000000000001101 <+727>: 48 8d 35 28 0c 20 00 lea 0x200c28(%rip),%rsi # 0x201d30 <_ZTI2B2>
0x0000000000001108 <+734>: 48 89 c7 mov %rax,%rdi
0x000000000000110b <+737>: e8 c0 fb ff ff callq 0xcd0 <__dynamic_cast@plt>
0x0000000000001110 <+742>: eb 05 jmp 0x1117 <main()+749>
0x0000000000001112 <+744>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000001117 <+749>: 48 89 45 98 mov %rax,-0x68(%rbp)
First there is a NULL check, and it returns NULL if th einput is NULL.
Otherwise, it sets up some arguments in the RDX, RSI and RDI and calls __dynamic_cast
.
I don't have the patience to analyze this further now, but as others said, the only way for this to work is for __dynamic_cast
to access some extra RTTI in-memory data structures that represent the class hierarchy.
It must therefore start from the B2
entry for that table, then walk this class hierarchy until it finds that the vtable for a D
typecast from b2s[0]
.
This is why reinterpret cast is potentially expensive! Here is an example where a one liner patch converting a dynamic_cast
to a static_cast
in a complex project reduced runtime by 33%!.
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
this one just believes us blindly: we said there is a D
at address b2s[1]
, and the compiler does no offset calculations.
But this is wrong, because D is actually at 0x7fffffffc930, what is at 0x7fffffffc940 is the B2-like structure inside D! So trash gets accessed.
We can confirm this from the horrendous -O0
assembly that just moves the value around:
70 dp = reinterpret_cast<D*>(b2s[1]);
0x00000000000011fa <+976>: 48 8b 45 d8 mov -0x28(%rbp),%rax
0x00000000000011fe <+980>: 48 89 45 98 mov %rax,-0x68(%rbp)
Related questions:
Tested on Ubuntu 18.04 amd64, GCC 7.4.0.
While other answers nicely described all differences between C++ casts, I would like to add a short note why you should not use C-style casts (Type) var
and Type(var)
.
For C++ beginners C-style casts look like being the superset operation over C++ casts (static_cast<>(), dynamic_cast<>(), const_cast<>(), reinterpret_cast<>()) and someone could prefer them over the C++ casts. In fact C-style cast is the superset and shorter to write.
The main problem of C-style casts is that they hide developer real intention of the cast. The C-style casts can do virtually all types of casting from normally safe casts done by static_cast<>() and dynamic_cast<>() to potentially dangerous casts like const_cast<>(), where const modifier can be removed so the const variables can be modified and reinterpret_cast<>() that can even reinterpret integer values to pointers.
Here is the sample.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
The main reason why C++ casts were added to the language was to allow a developer to clarify his intentions - why he is going to do that cast. By using C-style casts which are perfectly valid in C++ you are making your code less readable and more error prone especially for other developers who didn't create your code. So to make your code more readable and explicit you should always prefer C++ casts over C-style casts.
Here is a short quote from Bjarne Stroustrup's (the author of C++) book The C++ Programming Language 4th edition - page 302.
This C-style cast is far more dangerous than the named conversion operators because the notation is harder to spot in a large program and the kind of conversion intended by the programmer is not explicit.
Does this answer your question?
I have never used reinterpret_cast
, and wonder whether running into a case that needs it isn't a smell of bad design. In the code base I work on dynamic_cast
is used a lot. The difference with static_cast
is that a dynamic_cast
does runtime checking which may (safer) or may not (more overhead) be what you want (see msdn).
In addition to the other answers so far, here is unobvious example where static_cast
is not sufficient so that reinterpret_cast
is needed. Suppose there is a function which in an output parameter returns pointers to objects of different classes (which do not share a common base class). A real example of such function is CoCreateInstance()
(see the last parameter, which is in fact void**
). Suppose you request particular class of object from this function, so you know in advance the type for the pointer (which you often do for COM objects). In this case you cannot cast pointer to your pointer into void**
with static_cast
: you need reinterpret_cast<void**>(&yourPointer)
.
In code:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
//static_cast<void**>(&pNetFwPolicy2) would give a compile error
reinterpret_cast<void**>(&pNetFwPolicy2) );
However, static_cast
works for simple pointers (not pointers to pointers), so the above code can be rewritten to avoid reinterpret_cast
(at a price of an extra variable) in the following way:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
&tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
Use dynamic_cast
for converting pointers/references within an inheritance hierarchy.
Use static_cast
for ordinary type conversions.
Use reinterpret_cast
for low-level reinterpreting of bit patterns. Use with extreme caution.
Use const_cast
for casting away const/volatile
. Avoid this unless you are stuck using a const-incorrect API.
(A lot of theoretical and conceptual explanation has been given above)
Below are some of the practical examples when I used static_cast, dynamic_cast, const_cast, reinterpret_cast.
(Also referes this to understand the explaination : http://www.cplusplus.com/doc/tutorial/typecasting/)
static_cast :
OnEventData(void* pData)
{
......
// pData is a void* pData,
// EventData is a structure e.g.
// typedef struct _EventData {
// std::string id;
// std:: string remote_id;
// } EventData;
// On Some Situation a void pointer *pData
// has been static_casted as
// EventData* pointer
EventData *evtdata = static_cast<EventData*>(pData);
.....
}
dynamic_cast :
void DebugLog::OnMessage(Message *msg)
{
static DebugMsgData *debug;
static XYZMsgData *xyz;
if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
// debug message
}
else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
// xyz message
}
else/* if( ... )*/{
// ...
}
}
const_cast :
// *Passwd declared as a const
const unsigned char *Passwd
// on some situation it require to remove its constness
const_cast<unsigned char*>(Passwd)
reinterpret_cast :
typedef unsigned short uint16;
// Read Bytes returns that 2 bytes got read.
bool ByteBuffer::ReadUInt16(uint16& val) {
return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
While other answers nicely described all differences between C++ casts, I would like to add a short note why you should not use C-style casts (Type) var
and Type(var)
.
For C++ beginners C-style casts look like being the superset operation over C++ casts (static_cast<>(), dynamic_cast<>(), const_cast<>(), reinterpret_cast<>()) and someone could prefer them over the C++ casts. In fact C-style cast is the superset and shorter to write.
The main problem of C-style casts is that they hide developer real intention of the cast. The C-style casts can do virtually all types of casting from normally safe casts done by static_cast<>() and dynamic_cast<>() to potentially dangerous casts like const_cast<>(), where const modifier can be removed so the const variables can be modified and reinterpret_cast<>() that can even reinterpret integer values to pointers.
Here is the sample.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
The main reason why C++ casts were added to the language was to allow a developer to clarify his intentions - why he is going to do that cast. By using C-style casts which are perfectly valid in C++ you are making your code less readable and more error prone especially for other developers who didn't create your code. So to make your code more readable and explicit you should always prefer C++ casts over C-style casts.
Here is a short quote from Bjarne Stroustrup's (the author of C++) book The C++ Programming Language 4th edition - page 302.
This C-style cast is far more dangerous than the named conversion operators because the notation is harder to spot in a large program and the kind of conversion intended by the programmer is not explicit.
Does this answer your question?
I have never used reinterpret_cast
, and wonder whether running into a case that needs it isn't a smell of bad design. In the code base I work on dynamic_cast
is used a lot. The difference with static_cast
is that a dynamic_cast
does runtime checking which may (safer) or may not (more overhead) be what you want (see msdn).
Source: Stackoverflow.com