static_cast vs dynamic_cast vs C-style cast: how not to break your code

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 type casting in C++ is a minefield

\n\n

Have you ever written something like (int)someFloat and hoped everything would be fine? Or maybe you've encountered a situation where the program compiles but crashes at runtime with a mysterious error? Welcome to the world of type casting in C++ — a place where even experienced developers lose hours debugging.

\n\n

The question "Regular cast vs. static_cast vs. dynamic_cast" on Stack Overflow has gathered tens of thousands of views. And this is no coincidence. Each type of cast is a tool with its own purpose, and using the wrong tool can lead to undefined behavior, memory leaks, or simply non-working code.

\n\n

In this article, we will break down the three main ways of type casting in C++, show the most common errors, and give clear recommendations on how to avoid them. By the end, you will know exactly which cast to use in each specific situation.

\n\n

1. Regular cast (C-style cast) — a legacy of the past

\n\n

The good old C-style cast is the simplest, yet the most dangerous way of casting. It looks like this: (NewType)expression. In C++, it is considered "regular," but its behavior can be very surprising.

\n\n

How does it work?

\n

The C++ compiler tries to interpret the C-style cast as a sequence of static_cast, const_cast, and reinterpret_cast. It selects the first suitable combination. The problem is that you never know exactly which cast will end up being applied.

\n\n

Example of an error: accidentally changing constness

\n
const int value = 42;\nint* ptr = (int*)&value;  // C-style cast removes const\n*ptr = 100;               // Undefined behavior!\ncout << value;           // May output 42, 100, or anything
\n\n

This code compiles without errors but leads to undefined behavior. const_cast would be more honest here — it at least shows your intention.

\n\n

When is C-style cast still acceptable?

\n
    \n
  • Never in modern C++ (C++11 and newer).
  • \n
  • Only for very simple numeric conversions where the risk is minimal (e.g., (float)intValue), but even then it's better to use static_cast.
  • \n
\n\n

Conclusion: forget about C-style cast. It makes code unreadable and unsafe. Modern compilers may even issue warnings for such constructs.

\n\n

2. static_cast — the universal soldier

\n\n

static_cast is the most commonly used type of cast in C++. It performs a compile-time check and is suitable for most situations where the type conversion is logically correct.

\n\n

When to use static_cast?

\n
    \n
  • Converting numeric types: double → int, int → float.
  • \n
  • Upcast (casting a pointer to a derived class to a pointer to a base class).
  • \n
  • Explicitly converting void* back to a specific type.
  • \n
  • Calling constructors with a single argument (but it's better to use explicit).
  • \n
\n\n

Example: correct upcast

\n
class Base { public: virtual void speak() { cout << "Base"; } };\nclass Derived : public Base { public: void speak() override { cout << "Derived"; } };\n\nDerived d;\nBase* b = static_cast<Base*>(&d);  // upcast — safe\nb->speak();  // Outputs "Derived" thanks to the virtual function
\n\n

Top error #1: Slicing when passing by value

\n

One of the most insidious errors is slicing. It occurs when you cast an object of a derived class to a base class by value, rather than by reference or pointer

Blogs

Book Recommendations