README.md
December 5, 2022 · View on GitHub
Description
A tutorial that explains and gives examples for static_cast, reinterpret_cast, const_cast, and dynamic_cast.
More Info
| Submitted On | |
| By | Andrew Hull |
| Level | Intermediate |
| User Rating | 4.9 (69 globes from 14 users) |
| Compatibility | C++ (general), Microsoft Visual C++, Borland C++, UNIX C++ |
| Category | Miscellaneous |
| World | C / 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 = #
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-<