C++ Safe downcasting


Example

Suppose that you have a pointer to an object of a polymorphic class:

Shape *ps;                       // see example on defining a polymorphic class
ps =  get_a_new_random_shape();  // if you don't have such a function yet, you 
                                 // could just write ps = new Square(0.0,0.0, 5);

a downcast would be to cast from a general polymorphic Shape down to one of its derived and more specific shape like Square or Circle.

Why to downcast ?

Most of the time, you would not need to know which is the real type of the object, as the virtual functions allow you to manipulate your object independently of its type:

std::cout << "Surface: " << ps->get_surface() << std::endl; 

If you don't need any downcast, your design would be perfect.

However, you may need sometimes to downcast. A typical example is when you want to invoke a non virtual function that exist only for the child class.

Consider for example circles. Only circles have a diameter. So the class would be defined as :

class Circle: public Shape { // for Shape, see example on defining a polymorphic class
    Point center;
    double radius;
public: 
    Circle (const Point& center, double radius)
       : center(center), radius(radius) {}

    double get_surface() const override { return r * r * M_PI; }   

    // this is only for circles. Makes no sense for other shapes 
    double get_diameter() const { return 2 * r; }
};

The get_diameter() member function only exist for circles. It was not defined for a Shape object:

Shape* ps = get_any_shape();
ps->get_diameter(); // OUCH !!! Compilation error 

How to downcast ?

If you'd know for sure that ps points to a circle you could opt for a static_cast:

std::cout << "Diameter: " << static_cast<Circle*>(ps)->get_diameter() << std::endl;

This will do the trick. But it's very risky: if ps appears to by anything else than a Circle the behavior of your code will be undefined.

So rather than playing Russian roulette, you should safely use a dynamic_cast. This is specifically for polymorphic classes :

int main() {
    Circle circle(Point(0.0, 0.0), 10);
    Shape &shape = circle;

    std::cout << "The shape has a surface of " << shape.get_surface() << std::endl;

    //shape.get_diameter();   // OUCH !!! Compilation error 

    Circle *pc = dynamic_cast<Circle*>(&shape); // will be nullptr if ps wasn't a circle 
    if (pc) 
        std::cout << "The shape is a circle of diameter " << pc->get_diameter() << std::endl;
    else
        std::cout << "The shape isn't a circle !" << std::endl; 
}        

Note that dynamic_cast is not possible on a class that is not polymorphic. You'd need at least one virtual function in the class or its parents to be able to use it.