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 Object
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?
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.