Click here to Skip to main content
15,881,092 members
Articles / Programming Languages / C++/CLI
Article

Casting Basics - Use C++ casts in your VC++.NET programs

Rate me:
Please Sign up or sign in to vote.
4.93/5 (21 votes)
23 Aug 2002CPOL10 min read 349.1K   60   69
Demonstrates and compares the various casting operators available. Also suggests when to use and what to use!

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

MC++
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 :-

MC++
T t = static_cast<T>(expression);

An example usage would be something like :-

MC++
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

MC++
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 :-

MC++
T t = dynamic_cast<T>(expression);

A sample usage of dynamic_cast would be like :-

MC++
void Hello(Base* p1)
{
    //...
    Child *c1 = dynamic_cast<Child*>(p1);
    if(c1)
    {
        // we can safely use c1
    }
    //...
}

const_cast

The const_cast operator is used to remove the const, volatile, and __unaligned attributes) from a class.

MC++
__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 :-

MC++
T t = const_cast<T>(expression);

A sample use would be something similar to :-

MC++
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

MC++
reinterpret_cast
operator to convert from one type to any other type. It's syntax is :-

MC++
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.

MC++
/* A and B are unrelated types */
A *a = reinterpret_cast<A*>(new B());
B *b = reinterpret_cast<B*>(a); //safe to use b

__try_cast

__try_cast is a managed extension keyword not available in unmanaged C++. It's essentially similar to

MC++
dynamic_cast
except that it throws an exception on failure whereas dynamic_cast returns NULL. The syntax is :-

MC++
T t = __try_cast<T>(expression);

The exception that gets thrown is

MC++
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 :-

MC++
try
{
    p2 = __try_cast<Base*>(p1);
    //use p2 safely now
    //...
}
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.

MC++
/*
Our test base class
*/
__gc class Base
{
public:
    int dummy;
};

/*
Our test derived class
*/
__gc class Child : public Base
{
public:
    char anotherdummy;
};

/*
An independent test class
*/
__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

MC++
BaseToChild
which does various kinds of downcasting based on the
MC++
CastType
enumeration parameter passed to it. Obviously I cannot use
MC++
const_cast
here because we are casting across unlike types.

MC++
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:
        /* This won't compile */
        //retptr = const_cast<Child*>(p);
        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

MC++
Base *pBase1 = new Base();
Child *pChild1 = NULL;

I have

MC++
pBase1
which holds a
MC++
Base
object and I am now going to attempt to downcast it to a
MC++
Child
object. Obviously this is unsafe and something which I really shouldn't be doing. I called
MC++
BaseToChild
four times, once for each type of cast (const_cast not usable here).

MC++
//dynamic_cast
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

MC++
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
MC++
dynamic_cast
Failure Fails and returns
MC++
NULL
MC++
static_cast
Success Unsafe cast made
MC++
reinterpret_cast
Success Unsafe cast made
MC++
__try_cast
Failure Fails and throws an exception

Safe downcasting

MC++
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

MC++
BaseToChild
four times, once for each type of cast except
MC++
const_cast
. The table below shows the results I got.

Cast Result Notes
MC++
dynamic_cast
Success Safe cast
MC++
static_cast
Success Safe cast
MC++
reinterpret_cast
Success Safe cast
MC++
__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

MC++
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*
MC++
static_cast
and
MC++
reinterpret_cast
both of which are quite dangerous here. You may use either
MC++
dynamic_cast
or
MC++
__try_cast
depending on whether you like to check for
MC++
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

MC++
ChildToBase
. Just as previously, I cannot use
MC++
const_cast
here because we are casting across unlike types.

MC++
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:
        /* This won't compile */
        //retptr = const_cast<Base*>(p);
        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

MC++
Base *pBase2 = NULL;
Child *pChild2 = new Child();

I am casting from

MC++
Child*
to
MC++
Base*
which is perfectly okay and safe. I show below the results I got for each cast operator.

Cast Result Notes
MC++
dynamic_cast
Success Safe cast
MC++
static_cast
Success Safe cast
MC++
reinterpret_cast
Success Safe cast
MC++
__try_cast
Success Safe cast

Unsafe upcasting

MC++
Base *pBase2 = NULL;
/* Intentionally create a bad pointer */
Child *pChild2 = reinterpret_cast<Child*>(new Base2()); 

Here I have intentionally created a

MC++
Base2
object and cast it to a
MC++
Child
object using
MC++
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
MC++
dynamic_cast
Failure Fails and returns
MC++
NULL
MC++
static_cast
Success Unsafe cast made
MC++
reinterpret_cast
Success Unsafe cast made
MC++
__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

MC++
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
MC++
dynamic_cast
or
MC++
__try_cast
. In addition keep in kind that upcasting would probably be implicitly done in most situations.

const_cast usage

Consider the code below :-

MC++
const Base *pB = new Base();
/* won't compile */
pB->dummy = 100;

You'll get a compiler error, because you are trying to modify a

MC++
const
object. Using
MC++
const_cast
, you can quickly overcome this problematic situation.

MC++
const_cast<Base*>(pB)->dummy = 100;
/* should show 100 on the console */
Console::WriteLine(pB->dummy);

Another useful application of

MC++
const_cast
comes in
MC++
const
member functions. When you mark a class member as
MC++
const
, you are telling the compiler that this function is a read-only function that will not modify the object. This means your
MC++
this
pointer is now a
MC++
const Class * const
instead of just
MC++
Class * const
. Now suppose that for some reason you want to modify some member from this member function. You can use
MC++
const_cast
as shown below. Of course you might also want to rethink your design if you are having to un-const all your
MC++
const
member functions.

MC++
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

MC++
reinterpret_cast
. In my example,
MC++
Base2
and
MC++
Base
are two managed classes that do not share an inheritance chain except for deriving automatically from
MC++
System::Object
. Assume that for whatever reason, we need to cast a
MC++
Base2
object into a
MC++
Base
object and later cast it back to a
MC++
Base2
object.

MC++
Base *pB1 = NULL;
Base2 *pB2 = new Base2();

Let's first try to use

MC++
dynamic_cast
:-

MC++
/* Compiles, but fails */
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

MC++
static_cast
:-

MC++
/* Won't compile */
//pB1 = static_cast<Base*>(pB2);

Okay, great! That won't even compile because the compile-time check fails. How about

MC++
__try_cast
:-

MC++
/* Compiles, but throws exception */
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

MC++
reinterpret_cast
now.

MC++
/* This is a sort of blind man's cast */
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.

MC++
/* Now we cast it back */
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Nish Nishant is a technology enthusiast from Columbus, Ohio. He has over 20 years of software industry experience in various roles including Chief Technology Officer, Senior Solution Architect, Lead Software Architect, Principal Software Engineer, and Engineering/Architecture Team Leader. Nish is a 14-time recipient of the Microsoft Visual C++ MVP Award.

Nish authored C++/CLI in Action for Manning Publications in 2005, and co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is experienced in technology leadership, solution architecture, software architecture, cloud development (AWS and Azure), REST services, software engineering best practices, CI/CD, mentoring, and directing all stages of software development.

Nish's Technology Blog : voidnish.wordpress.com

Comments and Discussions

 
Generalreinterpret_cast and a real-world use for it. [modified] Pin
leeor_net11-Jan-10 23:11
leeor_net11-Jan-10 23:11 
QuestionHow did you know that it's unsafe? Pin
Akram Ben Hassan10-Oct-06 12:00
Akram Ben Hassan10-Oct-06 12:00 
Questionstatic_cast and multiple inheritance? Pin
Ioan Bizau10-Jul-06 11:44
Ioan Bizau10-Jul-06 11:44 
GeneralCompiler Warning C4312 Pin
Nguyen Phuoc Dai23-Dec-05 17:32
Nguyen Phuoc Dai23-Dec-05 17:32 
GeneralRe: Compiler Warning C4312 Pin
leeor_net28-Aug-11 14:16
leeor_net28-Aug-11 14:16 
Generalreinterpret_cast isn't a drop-in replacement for a C-style cast Pin
Don Clugston10-Nov-04 12:35
Don Clugston10-Nov-04 12:35 
GeneralNice Job Nish Pin
Nick Parker3-May-03 9:25
protectorNick Parker3-May-03 9:25 
Generalbeware of dynamic_cast<> Pin
Swinefeaster28-Oct-02 10:29
Swinefeaster28-Oct-02 10:29 
Just want to point out that I've had some serious issues with using dynamic_cast<> on invalid pointers. Instead of just failing and returning NULL, this actually corrupt memory! What I had to do was make up a hash table of valid object pointers and always look up the pointer before trying to do a dynamic_cast<>, and even then wrap the call in a try-catch block. The docs are all lies! Either that, or MS really buggered up their implementation of that cast.

swine

Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
GeneralRe: beware of dynamic_cast Pin
Suneet Chandok25-Aug-05 19:02
Suneet Chandok25-Aug-05 19:02 
Generalcast from unsigned char to Pin
Anonymous24-Oct-02 4:24
Anonymous24-Oct-02 4:24 
GeneralRe: cast from unsigned char to Pin
Nish Nishant24-Oct-02 5:24
sitebuilderNish Nishant24-Oct-02 5:24 
GeneralRe: cast from unsigned char to Pin
leeor_net28-Aug-11 14:18
leeor_net28-Aug-11 14:18 
General__unaligned Pin
W2k20-Sep-02 11:16
W2k20-Sep-02 11:16 
GeneralRe: __unaligned Pin
Nish Nishant20-Sep-02 14:07
sitebuilderNish Nishant20-Sep-02 14:07 
GeneralRe: __unaligned Pin
Tim Smith8-Oct-02 15:18
Tim Smith8-Oct-02 15:18 
GeneralRe: __unaligned Pin
Tim Smith8-Oct-02 15:26
Tim Smith8-Oct-02 15:26 
GeneralRe: __unaligned Pin
W2k11-Oct-02 5:56
W2k11-Oct-02 5:56 
Generalreinterpret_cast Pin
NormDroid3-Sep-02 8:51
professionalNormDroid3-Sep-02 8:51 
GeneralRe: reinterpret_cast Pin
Nish Nishant17-Sep-02 15:08
sitebuilderNish Nishant17-Sep-02 15:08 
GeneralRe: reinterpret_cast Pin
«_Superman_»19-Sep-02 20:40
professional«_Superman_»19-Sep-02 20:40 
GeneralRe: reinterpret_cast Pin
Nish Nishant20-Sep-02 14:08
sitebuilderNish Nishant20-Sep-02 14:08 
GeneralRe: reinterpret_cast Pin
Brian P. Adams17-Feb-04 11:16
Brian P. Adams17-Feb-04 11:16 
Questionhelp ? Pin
imran_rafique27-Aug-02 22:30
imran_rafique27-Aug-02 22:30 
AnswerRe: help ? Pin
Nish Nishant17-Sep-02 15:07
sitebuilderNish Nishant17-Sep-02 15:07 
GeneralCasting in JavaScript Pin
Alexandru Savescu25-Aug-02 21:50
Alexandru Savescu25-Aug-02 21:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.