0

I'm trying to figure out the behaviour of polymorphism with casting. Using the code below, are you able to explain to me why the "child > parent > child" instructions sequence is OK, and the "parent > child" sequence is not (chd3 variable is null after dynamic_cast) ?

#include <iostream>

using namespace std;

class Parent { 
    public: 
        virtual void speak() {};
};

class Child : public Parent {
    public:
        virtual void speak() override {
            cout << "Yeah !" << endl;
        };
};

int main(int argc, char* argv[]) {
    // ----------------------
    // child > parent > child : OK
    Child* chd1 = new Child;
    Parent* prt1 = dynamic_cast<Parent*>(chd1); if (prt1 == nullptr) { cout << "prt1 ERROR" << endl;  return 1; }
    prt1->speak();
    Child* chd2 = dynamic_cast<Child*>(prt1); if (chd2 == nullptr) { cout << "chd2 ERROR" << endl;  return 1; }
    chd2->speak();

    // parent > child : NOK : raises ptrnull value
    Parent* prt2 = new Parent;
    Child* chd3 = dynamic_cast<Child*>(prt2); if (chd3 == nullptr) { cout << "chd3 ERROR" << endl; return 1; };


    // debug
    cin.get();


    // return
    return 0;
}

Thanks a lot !

(Was expecting Parent > child dynamic_cast to be OK, because I found somewhere on the Internet that it would be OK if the classes have polymorphism.)

1 Answers1

1

The whole point of dynamic_cast is for it to examine the dynamic type of the object involved to determine whether the cast can succeed or not.

There's no need to use a dynamic_cast when going from Child * to Parent *. You can convert in that direction implicitly, so you don't need a cast at all.

Parent *p = new child; // no problem.

A dynamic_cast is useful for going the other direction. You have a Parent *, but you're not sure whether the thing it points at is a Parent or a Child. So you can attempt the conversion with a dynamic_cast, and it'll succeed if and only if the object that it's pointing at is a Child.

Parent *p1 = new Parent;
Parent *p2 = new Child;

Child *p3 = dynamic_cast<Child *>(p1); // should fail (give a null pointer)
Child *p4 = dynamic_cast<Child *>(p2); // should succeed.

Having said that, real uses for dynamic_cast are fairly unusual. The primary point of inheritance is to allow you to use a pointer (or reference) to the base class, but get behavior that's specific to the derived class, by declaring virtual functions in the base class, and implementing them in the derived class. dynamic_cast is primarily for cases where a derived class adds different functionality that's not in the base class at all.

class Person {
public:
    virtual void speak() = 0;
};

class Child : public Person { 
public:
    virtual void speak() { std::cout << "I'm a child\n"; }

    virtual void squabble() { std::cout << "kids squabble\n"; }
};

With a hierarchy like this, you can have some use for a dynamic_cast. Any Person can speak, but only a Child can squabble. So you might have something like this:

Person *p = new Child;
p->speak(); // no problem a Person can speak

// but only a child can squabble:
Child *c = dynamic_cast<Child *>(p);
if (c != nullptr)
    c->squabble();

But I'll repeat: uses for dynamic_cast are fairly unusual. If you end up using it much, chances are pretty good that you have a design problem.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111