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 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
  • When you need access to something that is derived-class specific
  • 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)
12.10 -- Printing inherited classes using operator<<
Index
12.8 -- Object slicing

8 comments to 12.9 — Dynamic casting

  • 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.

  • 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.

  • 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.

  • 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

Leave a Comment

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