Question

I read in a C++ book that you can use dynamic_cast to downcast a pointer to a base object to a derived object pointer, if the object it points to actually is that of the derived type. The format is dynamic_cast<Derived*>(basePointer). Furthermore, I read on this website that dynamic_cast should return a null pointer if the object it points to cannot be converted to the derived class type.

However, I recently tried having a pointer to a plain object with a virtual function downcast to an object that's derived from a different class, in order to call one of that class' functions. To my surprise, it worked:

#include <iostream>
using namespace std;

class Object {
    public:
        virtual ~Object () {}
};

class Base {
    public:
        virtual void doSomething () {
            cout << "Done something!" << endl;
        }
};

class Derived: public Base {
    public:
        virtual void doSomething () {
            cout << "You done goofed!" << endl;
        }
        void printFoo () {
            cout << "Foo" << endl;
        }

    private:
        int x;
};

int main () {
    Object o;
    Object* p = &o;

    if(dynamic_cast<Derived*>(p))
        cout << "Yep, baby is derived!" << endl;
    else
        cout << "Isn't derived." << endl;

    dynamic_cast<Derived*>(p)->printFoo();


    return 0;
}

Here, Base is a base class type designed to be used polymorphically (that is, it contains a virtual function), and Derived is a class type derived from Base. Object is just a plain class that is not related to either class, but its destructor is made virtual so that it can be used polymorphically. I'll explain the purpose of Derived::x in a moment. In the main function, an Object is created and a pointer to Object is assigned to its address. It checks whether Derived is a descendant of that pointer, and prints whether it is or it isn't. Then it casts that pointer to a pointer of type Derived, and Derived's printFoo function is called.

This shouldn't work, but it does. When I run it, it displays "Isn't derived," so it clearly understands that Obect is not Derived from Base, but then it prints "Foo" on the screen and exits without error. However, if I add the line x = 1; to the printFoo function, it exits with a Segmentation fault, as it's trying to assign a variable which doesn't exist in that object. Furthermore, this only seems to work with non-virtual functions. If I try to call the virtual function doSomething, for instance, I get the Segmentation fault before "You done goofed!" is ever printed out.

What's going on here? Shouldn't dynamic_cast<Derived*>(p) return a null pointer, so that trying to call printFoo from that would automatically cause an error? Why is that working when it shouldn't?

Was it helpful?

Solution

You've already established that dynamic_cast is returning a NULL pointer, so the real question is why function calls on a NULL object pointer appear to work in some cases but not others.

In ALL of these cases you're getting undefined behavior. Just remember that undefined doesn't always mean crash - sometimes you get perfectly reasonable results. You just can't rely on it.

Here's an explanation based on what's probably going on, but there are no guarantees it will work the same way tomorrow, much less when you change the optimization settings or get a new version of the compiler.

printFoo isn't virtual, so there's no need to use a vtable to access it. doSomething is virtual so it does need the vtable. A NULL pointer doesn't have a vtable so calling doSomething blows up immediately.

printFoo doesn't use any of the members of the object until you add the x = 1 line to it. As long as the compiler doesn't generate any code that accesses the this pointer, it's likely to work fine.

OTHER TIPS

It works because

dynamic_cast<Derived*>(p)->printFoo();

returns a null pointer as expected, but the null pointer is of type Derived. When it calls printFoo, it works because printFoo does not use any member variable, in which case it would refer to this for using that variable, and would crash for dereferencing null pointer. Since no member variable is used, no reference to this, no problem( that's why it gives segfault when u use x in the method).

It is same as following simple code:

class A{
  public:
   int x;
   void f1(){}
   void f2(){x=1;}
}

 A* x=nulptr;
 x->f1(); // will work
 x->f2(); // UB

You're accessing a null pointer without using any actual object state, which is safe though meaningless.

If you change Derived::printFoo() to the following, you'll actually be in "undefined behavior territory", and will very likely segfault:

void printFoo () {
  cout << "Foo: " << x << endl;
}

For what it's worth, using references instead of pointers will make this throw an exception if you'd rather propagate the failure outward than test and deal with it locally:

dynamic_cast<Derived &>(*p).printFoo(); // kaboom! throws std::bad_cast
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top