Way back in lesson 6.16 -- Explicit type conversion (casting) and static_cast, we examined the concept of casting, and the use of static_cast to convert variables from one type to another.
In this lesson, we’ll continue by examining another type of cast: dynamic_cast.
The need for dynamic_cast
When dealing with polymorphism, you’ll often encounter cases where you have a pointer to a base class, but you want to access some information that exists only in a derived class.
Consider the following (slightly contrived) program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <iostream> #include <string> class Base { protected: int m_value{}; public: Base(int value) : m_value{value} { } virtual ~Base() = default; }; class Derived : public Base { protected: std::string m_name{}; public: Derived(int value, const std::string& name) : Base{value}, m_name{name} { } const std::string& getName() const { return m_name; } }; Base* getObject(bool bReturnDerived) { if (bReturnDerived) return new Derived{1, "Apple"}; else return new Base{2}; } int main() { Base *b{ getObject(true) }; // how do we print the Derived object's name here, having only a Base pointer? delete b; return 0; } |
In this program, function getObject() always returns a Base pointer, but that pointer may be pointing to either a Base or a Derived object. In the case where the pointer is pointing to a Derived object, how would we call Derived::getName()?
One way would be to add a virtual function to Base called getName() (so we could call it with a Base pointer/reference, and have it dynamically resolve to Derived::getName()). But what would this function return if you called it with a Base pointer/reference that was actually pointing to a Base object? There isn’t really any value that makes sense. Furthermore, we would be polluting our Base class with things that really should only be the concern of the Derived class.
We know that C++ will implicitly let you convert a Derived pointer into a Base pointer (in fact, getObject() does just that). This process is sometimes called upcasting. However, what if there was a way to convert a Base pointer back into a Derived pointer? Then we could call Derived::getName() directly using that pointer, and not have to worry about virtual function resolution at all.
dynamic_cast
C++ provides a casting operator named dynamic_cast that can be used for just this purpose. Although dynamic casts have a few different capabilities, by far the most common use for dynamic casting is for converting base-class pointers into derived-class pointers. This process is called downcasting.
Using dynamic_cast works just like static_cast. Here’s our example main() from above, using a dynamic_cast to convert our Base pointer back into a Derived pointer:
1 2 3 4 5 6 7 8 9 10 11 12 |
int main() { Base *b{ getObject(true) }; Derived *d{ dynamic_cast<Derived*>(b) }; // use dynamic cast to convert Base pointer into Derived pointer std::cout << "The name of the Derived is: " << d->getName() << '\n'; delete b; return 0; } |
This prints:
The name of the Derived is: Apple
dynamic_cast failure
The above example works because b is actually pointing to a Derived object, so converting b into a Derived pointer is successful.
However, we’ve made quite a dangerous assumption: that b is pointing to a Derived object. What if b wasn’t pointing to a Derived object? This is easily tested by changing the argument to getObject() from true to false. In that case, getObject() will return a Base pointer to a Base object. When we try to dynamic_cast that to a Derived, it will fail, because the conversion can’t be made.
If a dynamic_cast fails, the result of the conversion will be a null pointer.
Because we haven’t checked for a null pointer result, we access d->getName(), which will try to dereference a null pointer, leading to undefined behavior (probably a crash).
In order to make this program safe, we need to ensure the result of the dynamic_cast actually succeeded:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
int main() { Base *b{ getObject(true) }; Derived *d{ dynamic_cast<Derived*>(b) }; // use dynamic cast to convert Base pointer into Derived pointer if (d) // make sure d is non-null std::cout << "The name of the Derived is: " << d->getName() << '\n'; delete b; return 0; } |
Rule
Always ensure your dynamic casts actually succeeded by checking for a null pointer result.
Note that because dynamic_cast does some consistency checking at runtime (to ensure the conversion can be made), use of dynamic_cast does incur a performance penalty.
Also note that there are several cases where downcasting using dynamic_cast will not work:
1) With protected or private inheritance.
2) For classes that do not declare or inherit any virtual functions (and thus don’t have a virtual table).
3) In certain cases involving virtual base classes (see this page for an example of some of these cases, and how to resolve them).
Downcasting with static_cast
It turns out that downcasting can also be done with static_cast. The main difference is that static_cast does no runtime type checking to ensure that what you’re doing makes sense. This makes using static_cast faster, but more dangerous. If you cast a Base* to a Derived*, it will “succeed” even if the Base pointer isn’t pointing to a Derived object. This will result in undefined behavior when you try to access the resulting Derived pointer (that is actually pointing to a Base object).
If you’re absolutely sure that the pointer you’re downcasting will succeed, then using static_cast is acceptable. One way to ensure that you know what type of object you’re pointing to is to use a virtual function. Here’s one (not great because it uses a global variable) way to do that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#include <iostream> #include <string> // Class identifier enum class ClassID { base, derived // Others can be added here later }; class Base { protected: int m_value{}; public: Base(int value) : m_value{value} { } virtual ~Base() = default; virtual ClassID getClassID() const { return ClassID::base; } }; class Derived : public Base { protected: std::string m_name{}; public: Derived(int value, const std::string& name) : Base{value}, m_name{name} { } const std::string& getName() const { return m_name; } virtual ClassID getClassID() const { return ClassID::derived; } }; Base* getObject(bool bReturnDerived) { if (bReturnDerived) return new Derived{1, "Apple"}; else return new Base{2}; } int main() { Base *b{ getObject(true) }; if (b->getClassID() == ClassID::derived) { // We already proved b is pointing to a Derived object, so this should always succeed Derived *d{ static_cast<Derived*>(b) }; std::cout << "The name of the Derived is: " << d->getName() << '\n'; } delete b; return 0; } |
But if you’re going to go through all of the trouble to implement this (and pay the cost of calling a virtual function and processing the result), you might as well just use dynamic_cast.
dynamic_cast and references
Although all of the above examples show dynamic casting of pointers (which is more common), dynamic_cast can also be used with references. This works analogously to how dynamic_cast works with pointers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <iostream> #include <string> class Base { protected: int m_value; public: Base(int value) : m_value{value} { } virtual ~Base() = default; }; class Derived : public Base { protected: std::string m_name; public: Derived(int value, const std::string& name) : Base{value}, m_name{name} { } const std::string& getName() const { return m_name; } }; int main() { Derived apple{1, "Apple"}; // create an apple Base &b{ apple }; // set base reference to object Derived &d{ dynamic_cast<Derived&>(b) }; // dynamic cast using a reference instead of a pointer std::cout << "The name of the Derived is: " << d.getName() << '\n'; // we can access Derived::getName through d return 0; } |
Because C++ does not have a “null reference”, dynamic_cast can’t return a null reference upon failure. Instead, if the dynamic_cast of a reference fails, an exception of type std::bad_cast is thrown. We talk about exceptions later in this tutorial.
dynamic_cast vs static_cast
New programmers are sometimes confused about when to use static_cast vs dynamic_cast. The answer is quite simple: use static_cast unless you’re downcasting, in which case dynamic_cast is usually a better choice. However, you should also consider avoiding casting altogether and just using virtual functions.
Downcasting vs virtual functions
There are some developers who believe dynamic_cast is evil and indicative of a bad class design. Instead, these programmers say you should use virtual functions.
In general, using a virtual function should be preferred over downcasting. However, there are times when downcasting is the better choice:
- When you can not modify the base class to add a virtual function (e.g. because the base class is part of the standard library)
- When you need access to something that is derived-class specific (e.g. an access function that only exists in the derived class)
- When adding a virtual function to your base class doesn’t make sense (e.g. there is no appropriate value for the base class to return). Using a pure virtual function may be an option here if you don’t need to instantiate the base class.
A warning about dynamic_cast and RTTI
Run-time type information (RTTI) is a feature of C++ that exposes information about an object’s data type at runtime. This capability is leveraged by dynamic_cast. Because RTTI has a pretty significant space performance cost, some compilers allow you to turn RTTI off as an optimization. Needless to say, if you do this, dynamic_cast won’t function correctly.
![]() |
![]() |
![]() |
I don't understand why d doesn't get deleted like b is. I get the feeling the solution is obvious but I don't see it.
`d` is `b`. There is only 1 object. Both `d` and `b` point to the same object.
Wow, thanks..
Guess I could use some sleep :\
Is that because for each 'new Derived' or 'new Base', we have a new object from a heap memory, so we need to just one 'delete' command, right? I mean variable 'b' has the address of Base part of that object created on the heap and and variable 'd' has the address of Derived part of the same object. Correct?
Also, there are some code inconsistency through the code examples in this lecture:
1: it should be 'const std::string& name'
2: all constructor initialization lists should use '{}' instead of '()'
3: all the instantiations in the main() section use '=' instead of '{}'
That's right
I've updated the lesson, thanks for pointing out the old code!
Typo. "What if b wasn’t pointing to a Derived object?", it should be "What if b wasn’t pointing to a Base object?"
It's correct as written. Did you mistake "wasn't" for "was"?
thanks a million man,although I haven't read all of his code and dunno why he had created so many functions and etc..they are actually scary looking and I don't have the confidence to try to understand them xd. after reading ur comment several times I can finally understand what you mean.what you mean is in a condition where functions cannot be inlined the static is a little more performant but the extra code isnot worth it.and also a generic question,should I worry about little stuff now ? or I should just leave my questions behind for now and try to be satisfied with the information I learn about here and later go back to learn deeplier?e.g also you have well written about dynamic_cast here I still don't know how it is performed how does it use vtable and why, and a lot of other things about it.
how did you walk this road yourself ?because I really cannot be satisfied with shallow learning about things.
> should I worry about little stuff now ?
If the little stuff only requires a minor change to your code, yes. For example ++prefix vs postfix++. In most cases, this is either none or only a minor performance difference, but it's 0 extra effort to use ++prefix, so use ++prefix. On the other hand you have code like the one you posted, where you'd have to replace 1 line with what, 30 lines? Those 30 lines are also a lot harder to maintain and it's a lot easier to make mistakes, so stay away from it until you need to go there.
> should just leave my questions behind for now and try to be satisfied with the information I learn about here
Question everything, but postpone it when you notice it's becoming too much. You're likely to learn about a better way or recognize why your idea wouldn't have worked out.
> you have well written about dynamic_cast here I still don't know how it is performed
It's not standardized how it is performed, only what it performs. The important part is that it doesn't change any values. A pointer stays a pointer and a reference stays a reference. All it has to do at runtime is to check if the types are compatible, then return the original pointer or a `nullptr` (Or throw).
The vtable contains information about the type. Taking `A` from my updated version of your code
Alongside the destructor(s) and the virtual member functions `a_`, `b_`, `c_`, there's a "typeinfo for A". If you have a `Base` pointer that points to an `A`, the vtable is the one of `A`, and in there it says that this object is an `A`.
You give a `Base*` to `dynamic_cast` and tell it to case to `A*`, the `dynamic_cast` looks into the vtable, sees that `Base*` points to an `A` because of the typeinfo in the vtable, and returns the same pointer back to you (With a different type). If the vtable doesn't say "typeinfo for A", `dynamic_cast` returns a `nullptr`.
> how did you walk this road yourself ?
Shallow learning is good for a start, because you'll learn which feature to use in which situation. You won't learn how things work under the hood and I don't think there's a single resource that can teach all this. You'll learn about details by running into problems.
Why is my code so slow?
Let me change this thing to that other thing.
It's faster now, why? I'll look it up and if I don't find it I ask someone.
Learned about vtables and `dynamic_cast` pow!
oh man, I'm really grateful and lucky to have found your website.
if you excuse me, now that you talked about vtable and casting I'd like to ask another question :), what is the type of __vptr that points to a vtable ?
I find another thing strange you say vtable is a static array but when I think about it, it seems impossible to me. a static array as I have learned in previous chapters needs a specific type to be specified at compile time. e.g ``std::array<FcnPointer,size> vtable``. however, I also know that variables are also disambiguated via vtable, and there is also a ``typeinfo for A`` which is of type a string or sth ? so all together we have 3 types of different variables. then how is vtable a static array?
The vtable is not a C++ feature, that's just the most common way virtual functions are implemented. With this not being a C++ standardized concept, compilers can do whatever they want to, eg. mixing types (All types in the vtable are pointers though). There is no `__vptr` in the language, so again it's up to the compiler what they do and how they do it.
With virtual functions dynamic_cast has already lost most of its use :)
The purpose of this benchmark was to benchmark the performance of using dynamic_cast in order to detect the underlying type. I suggested to Alek to use an enum in the base class or std::variant instead.
The "dynamic proper" benchmark doesn't seem to evaluate the cost of dynamic_cast at all... it just seems to evaluate the cost of virtual functions (to be honest I'm actually a bit impressed that virtual functions are able to perform so closely :O).
> The purpose of this benchmark was to benchmark the performance of using dynamic_cast in order to detect the underlying type
I understand, the issue is that this isn't a practical use case. You can use a broom's handle to push away dirt, but comparing that to using a vacuum cleaner doesn't make sense, because you're misusing the broom. So I turned the broom around to the way it's meant to be used to get a fair comparison.
If you need fast type access, a hybrid approach can be used as you suggested. That is, storing the type in the base directly to skip the vtable lookup.