Skip to content
C++ Better Explained
Go back
static_cast vs dynamic_cast in C++: Which One to Use

static_cast vs dynamic_cast in C++

Short answer: static_cast is checked by the compiler and costs nothing at runtime. dynamic_cast checks the actual object type while the program runs and fails safely if you’re wrong.

Derived* d1 = static_cast<Derived*>(basePtr);    // trust me, it's a Derived
Derived* d2 = dynamic_cast<Derived*>(basePtr);   // check — nullptr if not

The Core Difference

#include <iostream>

class Animal {
public:
    virtual ~Animal() = default;     // makes the class polymorphic
};

class Dog : public Animal {
public:
    void bark() { std::cout << "Woof\n"; }
};

class Cat : public Animal {
public:
    void meow() { std::cout << "Meow\n"; }
};

int main() {
    Animal* a = new Cat();           // a Cat, seen as an Animal

    Dog* d1 = static_cast<Dog*>(a);  // compiles, but a is NOT a Dog
    // d1->bark();                   // undefined behaviour — anything can happen

    Dog* d2 = dynamic_cast<Dog*>(a); // checks at runtime
    if (d2) d2->bark();
    else    std::cout << "Not a Dog\n";   // this runs

    delete a;
}

static_cast asks the compiler “is this conversion plausible?” The answer is yes — Dog does derive from Animal — so it compiles. But the object is really a Cat, and using d1 is undefined behaviour.

dynamic_cast asks at runtime “is this object actually a Dog?” It isn’t, so you get nullptr and a clean branch.

dynamic_cast Needs a Virtual Function

class Base { };                          // NOT polymorphic
class Derived : public Base { };

Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b);  // compile error

The compiler only stores runtime type information for polymorphic classes — those with at least one virtual function. Adding a virtual destructor fixes it and is good practice anyway:

class Base {
public:
    virtual ~Base() = default;           // now polymorphic
};

If you’re deleting derived objects through a base pointer you need that virtual destructor regardless — see virtual destructors in C++.

Learning C++ properly? The C++ Better Explained Ebook covers inheritance, polymorphism and casting in plain English — 87 pages, just $19.

Casting References Throws Instead

There is no null reference, so the reference form throws:

#include <stdexcept>
#include <typeinfo>

try {
    Dog& d = dynamic_cast<Dog&>(*a);
    d.bark();
} catch (const std::bad_cast& e) {
    std::cout << "Bad cast: " << e.what() << '\n';
}

Pointer form → nullptr on failure. Reference form → throws std::bad_cast.

What static_cast Is Actually For

Most static_cast uses have nothing to do with class hierarchies:

double d = 3.9;
int i = static_cast<int>(d);              // 3 — numeric conversion

int a = 7, b = 2;
double r = static_cast<double>(a) / b;    // 3.5, not 3

enum Colour { Red, Green };
int c = static_cast<int>(Green);          // 1

void* raw = malloc(sizeof(int));
int* p = static_cast<int*>(raw);          // void* back to a typed pointer

That integer-division case is the one beginners hit most — see C++ integer division.

Upcasting Doesn’t Need a Cast at All

Going up the hierarchy — derived to base — is always safe and implicit:

Dog* d = new Dog();
Animal* a = d;                      // no cast needed

You only need a cast going down, and that’s exactly where the choice matters.

The Honest Advice

If you find yourself reaching for dynamic_cast often, the design is usually asking for a virtual function instead:

// Instead of this
if (Dog* d = dynamic_cast<Dog*>(a))      d->bark();
else if (Cat* c = dynamic_cast<Cat*>(a)) c->meow();

// Prefer this
class Animal {
public:
    virtual ~Animal() = default;
    virtual void speak() = 0;
};
a->speak();                              // the object decides

That’s the whole point of polymorphism — see virtual functions and polymorphism. dynamic_cast is the escape hatch for when you genuinely can’t restructure.

Quick Reference

static_castdynamic_cast
Checked atCompile timeRuntime
Runtime costNoneSmall
Fails byUndefined behaviournullptr or bad_cast
Needs virtual functionNoYes
Numeric conversionsYesNo
Safe downcastingNoYes
Use whenType is guaranteedType is uncertain

Take Your C++ Further

If you want inheritance, polymorphism and casting explained properly rather than pieced together, the C++ Better Explained Ebook covers it in plain English. Just $19.

👉 Get the C++ Better Explained Ebook — $19


📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Share this post on:

Written by

Sahil Bora

Software Engineer. Author and creator of C++ Better Explained.


Previous Post
Getters and Setters in C++: How and When to Use Them
Next Post
How to Get the Size of a Vector in C++

Keep Learning