c++inheritancederived-class

Using pointer-to-derived on a base class object, which overridden methods are called?


I have a situation that I thought was going to cause problems but now I'm not so sure.

The code I'm looking at has a base and derived class with a method in the base that is over-ridden in the derived. It creates a base object and static casts that to the derived type:

Derived *d = static_cast< Derived * >( new Base( x, y, z ) );
d->overridden_method();

I thought that calling the overridden method in that case would result in the base class method being executed as the underlying object itself is of that class, and therefore the vtable entry would reference the base class function.

However, it looks like the derived method is being called in this case. Is that expected?

There's plenty of stuff I've found regarding going the other way (casting derived to base) but I've come up short trying to find any information on this scenario. Calling the overridden function on a base class pointer will, I believe, call the derived method. Otherwise, something like this wouldn't work, surely:

for ( const auto &shape : shapes ) {
    shape.draw();  // Handles any sort of derived shape.
}

By the way, before you state that we shouldn't be doing the cast to derived, be aware it's code we've inherited and we're actually trying to fix this issue (or non-issue if I'm wrong).

However, when I created a unit test to illustrate the problem (so I'd know when I'd fixed it), it looks like there is no problem. Hence this question.


Solution

  • Casting a pointer that actually points to a (complete) base object to a derived type is undefined behavior. Quote from expr.static.cast(emphasis mine)

    A prvalue of type “pointer to cv1 B”, where B is a class type, can be converted to a prvalue of type “pointer to cv2 D”, where D is a complete class derived from B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If B is a virtual base class of D or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]), the program is ill-formed. The null pointer value ([basic.compound]) is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1 B” points to a B that is actually a base class subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined.

    Your pointer does not point to a valid base-class subobject, hence the behavior is undefined.