Prologue
If someone were to ask me to name the single most exciting feature of C++,
I'd reply saying that it was the ability to perform casts from one type to
another. Of course this is purely a whimsical and highly irrational love I have
for casts and is not a reflection of any great technical pondering I might have
committed on casts and their usefulness. I mentally squeal with delight every
time I see code that looks something similar to ((CNishApp*)AfxGetApp())->CustomFunc()
or (LPSTR)(LPCTSTR)m_str
. This was all quite well when I was using VC++ 6 but
when I started moving some of my old applications to VC++.NET, it was a little
disconcerting to see all those compiler warnings about how my C style casts were
now deprecated.
That's when I took a look at the new C++ casting operators that were
available and quickly discovered that, they were a much safer and smarter way
for doing casts. This article will run through the various casting operators
that are available and will suggest when and where you might want to use each of
these casting operators. The entire article has been written from a managed
extensions context and therefore I only discuss managed classes in this article.
This has some interesting corollaries which I mention later in the article.
Dangers of C-style casts
The single biggest problem with C-style casts is that they are absolutely and
totally unsafe. No compile time or run time checks are made and you are left
with full freedom to dig your own grave, as deep as you want to. There is
nothing to stop you from casting a base class pointer that points to a base
class object to a derived class pointer, which means that when you make your
derived class method calls, catastrophe results most assuredly.
Another problem is that you never know what kind of cast you are trying to
achieve. All casts you can do look the same as far as syntax goes. Obviously
this can be a pain in the neck when you are debugging. In addition some people
use the functional style casts where they do int(x)
instead of
(int)x
and this results in further obfuscation of the code, because
C/C++ code is just about always sprinkled with functions and their accompanying
brackets.
The C++ casting operators
There are five casting operators provided which serve to replace the old
C-style casts. Each has a specific purpose and intended usage which I explain
later down the article. Before that I'd like to touch on the subject of
polymorphic classes. Any class that has virtual functions is a polymorphic
class. Now this marks a major distinction among classes in the old unmanaged
world. But in the new managed world of C++ coding, every class is polymorphic,
because all managed classes derive implicitly from System::Object
which has virtual methods of its own. Thus this renders every __gc
class as a polymorphic class. This allows us to exclusively use the safer
dynamic_cast
instead of static_cast
just about
everywhere, which was not possible in unmanaged C++.
static_cast
dynamic_cast
const_cast
reinterpret_cast
__try_cast
(managed extensions only)
static_cast
The static_cast
operator relies only on compile time
information. Run time checks are not done to ensure a safe cast. You can do both
downcasts and upcasts using static_cast
, but there are
dangers involved which I demonstrate later down the article. The syntax
for using the static_cast
operator is shown below :-
T t = static_cast<T>(expression);
An example usage would be something like :-
CMainFrame* pMF = static_cast<CMainFrame*>(AfxGetMainWnd());
dynamic_cast
The dynamic_cast
operator relies both on compile time
and run time information. When you attempt to do a cast using the
dynamic_cast
operator, a run time check is made to see if this is a safe
cast, and if the cast is unsafe then this the cast returns NULL
.
Thus by checking for NULL
, we can determine whether the
cast attempt was successful or not. The syntax is :-
T t = dynamic_cast<T>(expression);
A sample usage of dynamic_cast
would be like :-
void Hello(Base* p1)
{
Child *c1 = dynamic_cast<Child*>(p1);
if(c1)
{
}
}
const_cast
The const_cast
operator is used to remove the
const
, volatile
, and
__unaligned
attributes) from a class.
__unaligned
is a Microsoft extension to C++ and I am not quite sure of
what it's purpose is. You can use const_cast
only to cast between types that
differ only in their const-ness or volatility. The syntax for use is similar to
the other casts :-
T t = const_cast<T>(expression);
A sample use would be something similar to :-
void Abc(const Base* pB)
{
Xyz(const_cast<Base*>(pB));
}
void Xyz(Base* pB)
{
}
reinterpret_cast
To be really honest I have no clue why they have this one because as far as I
see it, its just as bad as the old C style casts except that it advertises it's
lack of safety quite explicitly. You can use the
reinterpret_cast
operator to convert from one type to any other type.
It's syntax is :-
T t = reinterpret_cast<T>(expression);
Basically the only safe place to use it would be where we cast from Type-1 to
Type-2 and later we cast back Type-2 to Type-1. Now we have the exact object
back as when we started this reinterpret_cast
business.
A *a = reinterpret_cast<A*>(new B());
B *b = reinterpret_cast<B*>(a);
__try_cast
__try_cast
is a managed extension keyword not
available in unmanaged C++. It's essentially similar to
dynamic_cast
except that it throws an exception on failure whereas
dynamic_cast
returns NULL
. The
syntax is :-
T t = __try_cast<T>(expression);
The exception that gets thrown is
System::InvalidCastException
and you better use a try
-catch
block if you are using __try_cast
unless you want to
see your program break in between, though that might actually be a good thing
when you are still developing it. A sample use is shown below :-
try
{
p2 = __try_cast<Base*>(p1);
}
catch(System::InvalidCastException*)
{
Console::WriteLine("blast!");
}
When to use and what?
Well, we've seen 5 different casting operators and they are supposed to be
replacements for the old C-style casts. It might be a little baffling to choose
the correct cast operator when you are coding. To be really honest, till
recently I had no clue when to use what! Anyway I have created certain imaginary
casting scenarios and used each of the cast operators to try and do the casts
[where permitted by the compiler] and I have made an attempt to demonstrate when
and why some cast operators are not suitable and when they are suitable. Of
course the recommendations are strictly my own and I do not claim that they
might be the most suitable options, but then I intend to keep updating the
article with valuable input from C++ gurus who happen to come across this
article.
Test classes
These are the test classes and enums that I have used in my experiments.
__gc class Base
{
public:
int dummy;
};
__gc class Child : public Base
{
public:
char anotherdummy;
};
__gc class Base2
{
};
enum CastType
{
tdynamic_cast,
tstatic_cast,
tconst_cast,
treinterpret_cast,
ttry_cast
};
Downcasting
Downcasting is when you convert from a base class pointer to a derived class
pointer. I have written a function called
BaseToChild
which does various kinds of downcasting based on the
CastType
enumeration parameter passed to it. Obviously I cannot use
const_cast
here because we are casting across unlike types.
Child* BaseToChild(Base* p,CastType casttype)
{
Child* retptr = NULL;
switch(casttype)
{
case tdynamic_cast:
retptr = dynamic_cast<Child*>(p);
break;
case tstatic_cast:
retptr = static_cast<Child*>(p);
break;
case tconst_cast:
break;
case treinterpret_cast:
retptr = reinterpret_cast<Child*>(p);
break;
case ttry_cast:
try
{
retptr = __try_cast<Child*>(p);
}
catch(System::InvalidCastException*)
{
}
break;
}
return retptr;
}
Unsafe downcasting
Base *pBase1 = new Base();
Child *pChild1 = NULL;
I have
pBase1
which holds a
Base
object and I am now going to attempt to downcast it to a
Child
object. Obviously this is unsafe and something which I really
shouldn't be doing. I called
BaseToChild
four times, once for each type of cast (const_cast
not usable here).
pChild1 = BaseToChild(pBase1,tdynamic_cast);
if(!pChild1)
{
Console::WriteLine("dynamic_cast - downcast failure");
}
else
{
Console::WriteLine("dynamic_cast - downcast success");
}
I only show the sample code for
dynamic_cast
, but the code for the other three casts are very much
similar. I summarize my results in the table below.
Cast |
Result |
Notes |
dynamic_cast |
Failure |
Fails and returns
NULL |
static_cast |
Success |
Unsafe cast made |
reinterpret_cast |
Success |
Unsafe cast made |
__try_cast |
Failure |
Fails and throws an exception |
Safe downcasting
Base *pBase1 = new Child();
Child *pChild1 = NULL;
Alright now we have the Base object holding a Child object. Now it's
perfectly safe to downcast to the derived class. Again I called
BaseToChild
four times, once for each type of cast except
const_cast
. The table below shows the results I got.
Cast |
Result |
Notes |
dynamic_cast |
Success |
Safe cast |
static_cast |
Success |
Safe cast |
reinterpret_cast |
Success |
Safe cast |
__try_cast |
Success |
Safe cast |
Recommendation for downcasting
If you are absolutely sure that the cast is going to be safe, you can use any
of the four cast operators above, but I'd suggest that you use
static_cast
because that'd be the most efficient way of casting. If you
are even a microscopic percentage unsure as to the safety of your cast, you must
simply *avoid*
static_cast
and
reinterpret_cast
both of which are quite dangerous here. You may use
either
dynamic_cast
or
__try_cast
depending on whether you like to check for
NULL
or you like to have an exception raised and handled.
Upcasting
Upcasting is when you cast from a derived class to one of the parent classes
in the inheritance chain. Usually upcasting is pretty much safe except in
certain rare situations that are actually a result of bad coding rather than
anything else. I demonstrate both scenarios below. Just like I had a function
for downcasting, I also have one for upcasting called
ChildToBase
. Just as previously, I cannot use
const_cast
here because we are casting across unlike types.
Base* ChildToBase(Child* p,CastType casttype)
{
Base* retptr = NULL;
switch(casttype)
{
case tdynamic_cast:
retptr = dynamic_cast<Base*>(p);
break;
case tstatic_cast:
retptr = static_cast<Base*>(p);
break;
case tconst_cast:
break;
case treinterpret_cast:
retptr = reinterpret_cast<Base*>(p);
break;
case ttry_cast:
try
{
retptr = __try_cast<Base*>(p);
}
catch(System::InvalidCastException*)
{
}
break;
}
return retptr;
}
Safe upcasting
Base *pBase2 = NULL;
Child *pChild2 = new Child();
I am casting from
Child*
to
Base*
which is perfectly okay and safe. I show below the results I got
for each cast operator.
Cast |
Result |
Notes |
dynamic_cast |
Success |
Safe cast |
static_cast |
Success |
Safe cast |
reinterpret_cast |
Success |
Safe cast |
__try_cast |
Success |
Safe cast |
Unsafe upcasting
Base *pBase2 = NULL;
Child *pChild2 = reinterpret_cast<Child*>(new Base2());
Here I have intentionally created a
Base2
object and cast it to a
Child
object using
reinterpret_cast
. This is a totally unsafe cast, but I have done this to
demonstrate what happens when you do an unsafe upcast. I show my results in the
table below :-
Cast |
Result |
Notes |
dynamic_cast |
Failure |
Fails and returns
NULL |
static_cast |
Success |
Unsafe cast made |
reinterpret_cast |
Success |
Unsafe cast made |
__try_cast |
Failure |
Fails and throws an exception |
Recommendations for upcasting
In most situations upcasting should be quite safe except when you have a bad
derived class pointer (bad in the sense that it points to the wrong object).
Therefore my recommendation for upcasting is to use
static_cast
which should be the most efficient. If your upcasts are
unsafe it's probably time for you to sit down and figure out what's going wrong
in your code rather than using
dynamic_cast
or
__try_cast
. In addition keep in kind that upcasting would probably be
implicitly done in most situations.
const_cast usage
Consider the code below :-
const Base *pB = new Base();
pB->dummy = 100;
You'll get a compiler error, because you are trying to modify a
const
object. Using
const_cast
, you can quickly overcome this problematic situation.
const_cast<Base*>(pB)->dummy = 100;
Console::WriteLine(pB->dummy);
Another useful application of
const_cast
comes in
const
member functions.
When you mark a class member as
const
, you are telling the compiler that this
function is a read-only function that will not modify the object. This means
your
this
pointer is now a
const Class * const
instead of just
Class * const
. Now suppose that for some reason you want to modify some
member from this member function. You can use
const_cast
as shown below. Of course you might also want to rethink your
design if you are having to un-const all your
const
member functions.
void A::abc() const
{
const_cast<A* const>(this)->m_total++;
}
reinterpret_cast usage
As I already mentioned earlier, this is the most unsafe of all the C++ cast
operators and it's probably best to avoid using it. But then when porting old
code, you might want to convert the old style casts to
reinterpret_cast
. In my example,
Base2
and
Base
are two managed classes that do not share an inheritance chain
except for deriving automatically from
System::Object
. Assume that for whatever reason, we need to cast a
Base2
object into a
Base
object and later cast it back to a
Base2
object.
Base *pB1 = NULL;
Base2 *pB2 = new Base2();
Let's first try to use
dynamic_cast
:-
pB1 = dynamic_cast<Base*>(pB2);
if(!pB1)
Console::WriteLine("dynamic_cast failed");
It compiles fine, but fails during run-time. Now let's try using
static_cast
:-
Okay, great! That won't even compile because the compile-time check fails.
How about
__try_cast
:-
try
{
pB1 = __try_cast<Base*>(pB2);
}
catch(System::InvalidCastException*)
{
Console::WriteLine("__try_cast has thrown an exception");
}
Just like dynamic_cast
this compiles fine too, but
throws an exception at run-time. That leaves us just
reinterpret_cast
now.
pB1 = reinterpret_cast<Base*>(pB2);
Oh boy! That compiled and also ran fine. No errors. pB1
which was declared as a Base
object now holds a
Base2
object. To test this, we can try to re-cast it
back using dynamic_cast
.
Base2* pDest = dynamic_cast<Base2*>(pB1);
if(pDest)
{
Console::WriteLine("Original pointer has been obtained");
}
Well, as you might have understood by now, it's safest to avoid using
reinterpret_cast
, but it does have it's uses and
without it, we'd still have had to use C-style casts when porting old code.
Conclusion
Well, the magic of casts haven't died yet, and casts live through the C++
cast operators and continue to enthrall C/C++ lovers all over the world. I am
not sure that everything I have suggested in the article is nice and proper. But
I trust that I'll get the required critical feedback to correct any erroneous
statements and recommendations I might have made.