Search

12.9 — Dynamic casting

Way back in lesson 4.4a -- Explicit type conversion (casting), we examined the concept of casting, and the use of static_cast to convert variables from 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:

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 object, and have it dynamically resolve to Derived::getName()). But what would this function return if you called it with 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:

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:

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 dynamic_cast will not work to downcast from a virtual base class, nor will it work with protected or private inheritance.

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:

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.

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.
12.10 -- Printing inherited classes using operator<<
Index
12.8 -- Object slicing

19 comments to 12.9 — Dynamic casting

  • Ivan

    Hi,
    According to your explanation, the main difference between dynamic_cast and static_cast - is that dynamic_cast does runtime type checking to ensure that what you’re doing makes sense.
    So the dynamic_cast makes additional checking BUT then suddenly we are explained, that it can fail and the result of the conversion will be a null pointer. The logical question is - if it is making a runtime checking, why are we not guarded from these cases, when the dynamic_cast returns null pointer ?

    • Alex

      dynamic_cast needs some way to tell you when you’re doing something that doesn’t make sense, so your program can react to that. It does so by returning a null pointer. That null pointer is the dynamic_cast’s way of saying, “this didn’t make any sense”. You can then do whatever you want with that information.

  • Hugh Mungus

    Hey Alex,

    On your last point, "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)", couldn’t you just make the virtual function a pure virtual one if you never plan to instantiate the base class?

    Also, I made some modifications to main() to output appropriate stuff to console based on what getObject() returned using the <typeinfo> header

    • Alex

      Yes, you can definitely use a pure virtual function, but only if you never plan to instantiate the base class. If you still need to instantiate the base class, then that solution doesn’t work. I’ve updated the lesson to make a note of this. Thanks for the thought!

  • apfelpektin

    the code from ‘dynamic_cast and references’ gives me an error:

    i guess the temporary is not an lvalue and doesn’t have (or give) an address, but i’m not shure how to resolve this. the following seems to work:

    ..but what was the code intended to be?

    • Alex

      Mistake on my part -- you’re not allowed to initialize a non-const reference with an r-value (even though Visual Studio lets you do so). I’ve updated the example to work around this by making sure the reference is being initialized with an lvalue.

  • Surya

    Can I use dynamic_cast on references? If yes, what happens if it fails?

    • Alex

      Yes, you can use dynamic_cast with references, and if it fails it will throw an exception of type std::bad_cast. I added an example of how to do so to the lesson.

      • dazedbrain

        "Because C++ does not have a “null reference”, dynamic_cast can’t return a null reference upon failure. Instead, if the static_cast of a reference fails, an exception of type std::bad_cast is thrown. We talk about exceptions later in this tutorial."

        Did you mean "if the dynamic_cast of a reference fails"?

        Or is it that both dynamic_cast and static_cast will throw an exception of type std::bad_cast?

        Thanks in advance

  • Mauricio Mirabetti

    Alex, throughout the code sections there are some blocks {  } being finished with semi-colon (e.g. virtual ~Base() {};) Just a typo, I know, but thought you might want to know.
    Best regards.
    Mauricio

  • Vladimir

    Hello Alex! I can’t compile first example. The compiler says that type is not polymorphic. I’m using qtcreator as an IDE and gcc. What can be the reason?

    • Alex

      I’m not sure -- the types are polymorphic. The program compiles under multiple different compilers, so I’m not sure what the issue is with yours.

  • Gajendra Gulgulia

    Hi Alex,

    I don’t really get the syntax:

    what does the keyword new after return do technically and what is the difference if we don’t use "new" and simply return the derived object?

    Thanks in advance

    • Alex

      This is essentially the same as:

      Just without using a temporary variable.

      If you don’t use new, then you’ll return a Derived object by value rather than by address.

  • Ninja

    Hey Alex, I have a question. So why did you make your getName() function return by reference?

    Wouldn’t that make it an l-value making it modifiable and chainable? (which we don’t want for a getter).

    Also, I’m slightly confused about the whole casting thing. So a derived object is made up of a base part and a derived part. So when you do this below, would it be upcasting? What is upcasting?

    Is b still made up of a base part and derived part? I’m guessing it would be or else we can’t use virtual functions to access derived functions. If we upcast it, does it mean it will only have the base part now and we can’t make it polymorphic (ie. object slicing)?

    • Alex

      I’ve updated getName() to return a const std::string&, for the reasons you suggest.

      Yes, what you suggest would be upcasting. First the Derived object is created, and then the Derived pointer is upcast to a Base pointer. The Base pointer points at the Base portion of the Derived object (the rest of the Derived object still exists, it’s just inaccessible directly through Base -- for this, we need virtual functions, or to downcast it back to a Derived pointer). The object isn’t sliced here, as the Derived portion of the object still exists.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter