Skip to Content
Course content

117: Type Casting: static_cast, dynamic_cast, const_cast, reinterpret_cast

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think of type casting like a set of different "clearance levels" at a security checkpoint. Sometimes you just need to show your ID to prove you're a resident (that's a standard conversion). Other times, you're claiming to be a high-level executive, and the guard actually has to call the home office to verify your identity before letting you into the vault (that's a runtime check). Then there's the guy who sneaks in by pretending a piece of cardboard is a security badge, hoping the guard is too tired to notice (that's a raw bit reinterpretation). In C++, using a C-style cast like (int)myFloat is like shouting "Just let me in!" without specifying which clearance you're using. It's dangerous because it tells the compiler to try any cast that might work, even the ones that will crash your program.

Here is how those security levels map to the four C++ casts:

  • static_cast is your standard ID. It's for conversions the compiler already knows how to handle safely.
  • dynamic_cast is the phone call to headquarters. it verifies that an object is actually what you say it is during the program's execution.
  • const_cast is a temporary permit. It lets you bypass a "do not touch" sign (the const qualifier) for a brief moment.
  • reinterpret_cast is the cardboard badge. It tells the compiler, "Ignore the type entirely; just treat these bits as if they were something else."

The Standard Switch: static_cast

This is the one you'll use 90% of the time. Use it for things that make sense logically, like converting a double to an int or navigating a class hierarchy when you are 100% sure of the type. I prefer this over C-style casts because if you try to static_cast two types that have absolutely no relationship, the compiler will slap your wrist immediately.

double pi = 3.14159;
int roundedPi = static_cast<int>(pi); // Clear, intentional truncation

Checking the Pedigree: dynamic_cast

Now, this is where things get interesting. When you're working with inheritance and polymorphism, you might have a pointer to a Base class, but you suspect it's actually a Derived class. dynamic_cast is the only cast that performs a check at runtime. If the cast fails, it returns nullptr (for pointers). Note: this only works if your base class has at least one virtual function; otherwise, the compiler has no "RTTI" (Run-Time Type Information) to check against.

class Entity { virtual void update() {} };
class Player : public Entity { void shoot() {} };

Entity* e = new Player(); 
Player* p = dynamic_cast<Player*>(e);

if (p) {
    p->shoot(); // Safe! We verified e is actually a Player.
}

Breaking the Rules: const_cast

I'll be honest: you should rarely need this. const_cast is used to add or remove the const qualifier from a variable. Why would you do this? Usually, it's when you're dealing with a legacy API that takes a non-const pointer, but you know for a fact that the function won't actually modify the data. Just be careful: if you const_cast a variable that was originally declared as const and then try to change its value, you've entered the realm of Undefined Behavior. Your program might crash, or it might just behave weirdly.

void legacy_api(char* str) { /* doesn't actually change str */ }

const char* myText = "Hello";
legacy_api(const_cast<char*>(myText)); // Stripping const to satisfy the API

The Wild West: reinterpret_cast

This is the most dangerous tool in the shed. reinterpret_cast doesn't change the data; it just tells the compiler to look at the same sequence of bits as if they were a different type. It's used for low-level systems programming, like mapping a hardware register address to a struct. If you use this in a high-level business application, you're probably doing something wrong. I've seen it used to "hack" pointers into integers for storage, but it's highly non-portable.

long address = 0xDEADBEEF;
int* ptr = reinterpret_cast<int*>(address); 
// We are now telling C++ that the memory at 0xDEADBEEF is an integer.
// Do this, and you'll likely get a Segmentation Fault unless you're writing a kernel.



📋 Practical Task

Exercise: Implementing a Game Entity Downcaster

You are building a game engine where all game objects inherit from a base class GameObject. You have a std::vector<GameObject*> containing a mix of Enemy and PowerUp objects. Your task is to write a function called processCombat that iterates through this list and calls the attack() method, but only if the object is actually an Enemy.

Requirements:

  • Create a base class GameObject with a virtual destructor.
  • Create a derived class Enemy with a method void attack().
  • Create a derived class PowerUp with a method void boost().
  • In your main loop, use the appropriate C++ cast to safely identify Enemy objects within a list of GameObject pointers.
  • Ensure that PowerUp objects are ignored by the attack() logic without causing a crash.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.