README.md

December 5, 2022 · View on GitHub

Casts Made Easy!

Description

A tutorial that explains and gives examples for static_cast, reinterpret_cast, const_cast, and dynamic_cast.

More Info

Submitted On
ByAndrew Hull
LevelIntermediate
User Rating4.9 (69 globes from 14 users)
CompatibilityC++ (general), Microsoft Visual C++, Borland C++, UNIX C++
CategoryMiscellaneous
WorldC / C++
Archive File

Source Code

Casts Made Easy!

 

In C, casting was easy. You could cast like you called a function:

int i = int('A');

You can also do this in C++. But the only reason it's there is for backwards compatibility with C. Casting like this will eventually cause havoc in a large program, and it also only provides support for primitive types. In C++, there is a set of 4 ANSI C++ casts: static_cast, reinterpret_cast, const_cast, and (the much feared) dynamic_cast. Here I will provide an explanation and example for each cast, to make them easier to understand and possibly save some programs using C casting :)


First, the simplest and most common, static_cast. This cast simply converts from one data type to another. The syntax is:

static_cast<new_type>(argument);

where new_type is the type to be converted to, and argument is the data you wish to convert. Converting my earlier example from C to C++ yields:

int i = static_cast<int>('A');

Note that new_type can be any data type, primitive or user-defined. argument can also be a variable:

char letter = 'A';
int i = static_cast<int>(letter);

That's all there really is to static_cast.


The next type of (and hardest to spell) cast is reinterpret_cast. Unlike static_cast, reinterpret_cast doesn't actually change any data, it causes the data to be reinterpreted, or looked at differently, by the compiler. The most common use of reinterpret_cast is casting a void* pointer, such as the one returned from malloc():

int* num = reinterpret_cast<int*>(malloc(100));

But then again, who needs malloc() when you've got new? reinterpret_cast can be dangerous, however, like in this example:

int num = 5;
int* pNum = &num;
double* pDouble = reinterpret_cast<double*>(pNum);

cout << *pDouble << endl;

This outputs integer data as if it were double (or floating point) data. Nothing but bad things can result from that! You'll probably get a lot of garbage printed to the screen.

The moral here? Be careful with reinterpret_cast!


Next we'll look at const_cast. const_cast is for adding/removing const from a variable. There usually isn't a reason to do this, and if there is, it's probably bad programming. However, every so often there's a situation that you just can't get around, and have to use const_cast. (sorta like goto). Here's a simple example:

void Display(int* data)
{
cout << *data << endl;
}

int main()
{
const int num = 5;
Display(const_cast<int*>(&num));

return 0;
}

If you don't use const_cast here, the compiler will give you an error along the lines of "no match for function..." because const data can't be passed into non-const function data. Redundant? Yes. Avoidable? Yes. Occasionally necessary? Yes.


Finally, it's time to tackle the one no one else wants to: dynamic_cast. There's a lot of confusion over what it does, why to use it, when to use it, etc. The formal (i.e. newbie-scaring) description says that it "is a polymorphic cast that verifies the runtime type of the object being cast". Ouch. To make it clearer, consider this example:

class Base
{
public
virtual void DoSomething() {cout << "Base" << endl;}
};

class Derived : public Base
{
public:
virtual void DoSomething() {cout << "Derived" << endl;}
};

int main()
{
Derived derived;
Base* pBase = &derived;
// the base pointer points to a derived object. Legal, but confusing.

Derived* pDerived = dynamic_cast<Derived*>(pBase);
// because pBase is actually a pointer to a Derived at runtime, the
// cast succeeds and pDerived is assigned the value of pBase

if (pDerived)
pDerived->DoSomething();

else
cout << "Bad cast" << endl;

return 0;
}

 

In this example, the pointer to Base was assigned the address of a Derived object. That's legal. Then, a pointer to a Derived is declared. A check is performed with dynamic_cast: if, at runtime, the argument (pBase) is of type new_type (Derived), then dynamic_cast returns a pointer to Derived with the value pBase. So, pDerived is assigned the value of a base pointer which without a cast would be impossible.

dynamic_cast returns NULL if the cast fails, so to prevent a memory leak ALWAYS check if the cast succeeded with an if...else. Classes used with dynamic_cast must have at least one virtual function.


Well, that's it. I hope you've learned something from all of this! Please leave any comments/feedback that come to mind, everything is appreciated! If you need anything cleared up, please feel free to email me at kavutitan26@yahoo.com. Enjoy!

->Andrew-<