static_cast, dynamic_cast, const_cast, reinterpret_cast: when to use what?

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: why doesn't C++ forgive type casting mistakes?



Hi there! If you've ever written in C++, you've definitely encountered a situation where the compiler complains about a type mismatch. And then, you probably remember the good old C-style cast: (int)x. Does it work? Yes. Is it safe? No. Professional? Even less so.



C++ has four built-in casting operators, and each one solves its own task: static_cast, dynamic_cast, const_cast, and reinterpret_cast. Using them correctly means writing code that not only works but is also understandable to other developers. And also doesn't crash at runtime with mysterious errors.



In this tutorial, we'll break down each cast with real examples, understand when to use which, and when it's better not to cast at all. Let's go!



static_cast: your reliable friend for ordinary conversions



static_cast is the most popular and safe cast. It is used for explicit conversions between related types: numeric types, pointers up/down the inheritance hierarchy (without runtime checking), enums to integers and vice versa.



When to use static_cast?

  • Converting int to double (and vice versa).
  • Converting pointers within the same class hierarchy (upcast is safe, downcast requires certainty).
  • Converting void* to a specific type.
  • Calling explicit constructors or conversion operators.


Example:

double pi = 3.14159;int approx = static_cast<int>(pi); // 3

class Base { public: virtual ~Base() = default; };class Derived : public Base { public: void foo() {} };

Base* b = new Derived;Derived* d = static_cast<Derived*>(b); // OK, but no checkd->foo();delete b;


Important: static_cast does not perform runtime type checking. If you are sure the object is indeed of that type, use it. If not, look at dynamic_cast.



dynamic_cast: safe downcast with checking



dynamic_cast is the only cast that works only with polymorphic classes (must have at least one virtual function). It checks at runtime whether a pointer/reference can be safely cast. If not, it returns nullptr (for pointers) or throws std::bad_cast (for references).



When to use dynamic_cast?

  • When you are not sure the object is exactly of the derived type.
  • When working with heterogeneous containers (e.g., a vector of Base*).
  • In design patterns (e.g., Visitor).


Example:

class Base { public: virtual ~Base() = default; };class Derived : public Base { public: void specific() {} };class Other : public Base {};

vector<Base*> objects = {new Derived, new Other, new Derived};

for (Base* obj : objects) { Derived* d = dynamic_cast<Derived*>(obj); if (d) { d->specific(); // safe only for Derived }}


Downside: dynamic_cast is slower than static_cast because it requires RTTI (Run-Time Type Information). Don't overuse it in performance-critical sections.



const_cast: when you need to remove constness (but be careful!)



const_cast is the only cast that can remove (or add) cv-qualifiers (const, volatile). Sounds cool, but in practice it is rarely used and only in extreme cases.



Why is it even needed? Sometimes you get a const reference to an object, but you need to call an old function that is not marked const, even though it doesn't modify the object. Or when working with C libraries.



Example (correct):

void printInt(const int* p) {    // old                

Blogs

Book Recommendations