4

I don't understand why this works. pReallyABase is a downcasted shared_pointer< Derived > which points to a base class instance.

I understand why the compiler lets me call pReallyABase->onlyForDerived() since I defined it as a derived class pointer, but why don't I get a runtime error when I try to invoke a derived class function using that pointer?

class Base { 
    public:
        virtual string whatAmI() {
            return "I am a Base";
        }
};

class Derived : public Base {
    public:
        virtual string whatAmI() {
            return "I am a Derived";
        }

        string onlyForDerived() {
            return "I can do Derived things";
        }
};


int main(int argc, char *argv[]) { 
    shared_ptr<Base> pBase = shared_ptr<Base>(new Base);
    shared_ptr<Derived> pReallyABase = static_pointer_cast<Derived>(pBase);

    cout << pReallyABase->whatAmI() << endl;
    cout << pReallyABase->onlyForDerived() << endl;  //Why does this work?
    return 0;
}

Results

I am a Base
I can do Derived things
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173

2 Answers2

2

This regards how the member function(non static and non virtual) is called in C++, the book "Inside C++ object model" has an explain on this:

One C++ design criterion is that a nonstatic member function at minimum must be as efficient as its analogous nonmember function. There should be no additional overhead for choosing the member function instance. This is achieved by internally transforming the member instance into the equivalent nonmember instance. After these transformations each of its invocations must also be transformed: For example:

obj.magnitude();

becomes

magnitude_7Point3dFv(&obj);

and

ptr->magnitude();

becomes

magnitude_7Point3dFv(ptr);

So as the example above, the function onlyForDerived doesn't use any member variables of the class Derived, so it works. But this is an undefined behavior, we should not depend on it.

Matt
  • 6,010
  • 25
  • 36
  • it seems to compile even if you define `int x=0` in `Derived` then increment it in `onlyForDerived`. – vsoftco Jul 30 '14 at 20:16
  • You should probably mention that this is the reason why it's likely to work on some implementations, but that the behaviour is undefined so anything could happen. – Ben Hymers Jul 30 '14 at 20:16
  • @BenHymers, you're right – Matt Jul 30 '14 at 20:17
  • @vsoftco, is the program running in debug mode or release mode? – Matt Jul 30 '14 at 20:19
  • Make the member virtual and even without accessing member vars, it will still likely exhibit a crash (on any reasonable compiler, anyway). The code generated for virtual vs. non-virtual method calls will be very telling in a disassembly of the presented code. – WhozCraig Jul 30 '14 at 20:20
  • @BenHymers yes indeed, even if it seem to work, if you try to initialize `x` in a constructor then increment it, you're out of luck. `x` will not be initialized by the constructor, however the code continues to compile under `g++`. – vsoftco Jul 30 '14 at 20:20
  • @Matt I'm compiling with `g++` from command line with no optimizations turned on by default, so it is not release but not debug either, since I don't use `-g` flag. – vsoftco Jul 30 '14 at 20:24
  • @vsoftco, in VS2010, it just crashes as this kind behavior lead to memory corruption. – Matt Jul 30 '14 at 20:27
  • It is undefined behaviour, so anything can happen :) – vsoftco Jul 30 '14 at 20:28
2

You're automatically getting undefined behavior by treating a Base object as a Derived so any behavior you see is legitimate.

In this case, since the compiler "knows" that the static type of the derived pointer is Derived, it's able to make the call to the function even though it's not legal to do so.

It doesn't raise a runtime error because it doesn't have to (undefined is undefined). That said, if the function were virtual it would probably crash in most implementations, and if the derived class had data that the derived function referred to it would probably either crash or generate unexpected outputs.

Mark B
  • 95,107
  • 10
  • 109
  • 188