4

I have the following code:

#include <iostream>
using namespace std;

class Base 
{
public:
    virtual void WhoAmI() const;
    typedef void (Base::*WhoPtr)() const;
};

class Derived : public Base 
{
public:
    virtual void WhoAmI() const;
};

void Derived::WhoAmI() const 
{
    cout << "I am the derived" << endl;
}

void Base::WhoAmI() const 
{
    cout << "I am the base" << endl;
}

int main() 
{
    Base::WhoPtr func = &Base::WhoAmI;
    Base theBase;
    (theBase.*func)();
    Derived theDerived;
    (theDerived.*func)();
    cin.get();
    return 0;
}   

Lets focus on the main:

int main() 
{
    Base::WhoPtr func = &Base::WhoAmI;
    Base theBase;
    (theBase.*func)();
    Derived theDerived;
    (theDerived.*func)();
    cin.get();
    return 0;
}   

We have a local variable func, who holds the address of Base::WhoAmI.

Also, we have Base and Derived objects.

On line 2, we call the pointed func from the base: (theBase.*func)().

I understand until now.

2 lines after, we call this from the derived: (theDerived.*func)().

It prints: I am the derived. Why?

Both WhoAmI are virtual, that mean that the call dependent by the pointed object, and not by the type.

the pointed object is func who belongs to Base. Why does it print I am the derived instead of I am the base?

Billie
  • 8,938
  • 12
  • 37
  • 67
  • "that mean that the call dependent by the pointed object, and not by the type." Calling a virtual member function selects an override. Which override is selected depends on the "dynamic type of the object expression" [expr.call]/1 in the class-member-access. Here, that's the type of `theDerived`. – dyp Jul 28 '13 at 17:13

3 Answers3

1

Why are you surprised. You have a pointer to member function that points to a virtual function. If you took the address of theDerived, or a reference to it, and initialized a Base* or a Base& with it, you'd expect ptrToBase->WhoAmI() to call the function in the derived class. This is, after all, why you use a virtual function to begin with. The same thing holds when you call through a pointer to member function. The expression &Base::WhoAmI yields a pointer to (virtual) member function.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

The pointed object is theDerived. The method you selected is Base::whoAmI, note that the method name contains a class reference (static) but not an object reference (dynamic). What virtual function to call is determined by the run-type time of the object that is used as this for the method.

tohava
  • 5,344
  • 1
  • 25
  • 47
0

The whole point about virtual functions is, that it is a runtime decision which version is called based on the dynamic type of the object in question. This is very different of a nonvirtual function call, in which the compiler itself makes a decision based on the declared type of the object, irrespective of what type the actual runtime object is of.

To accomplish this, each class has a table of virtual functions (a vtable), that all its instances have an implicit pointer to at runtime. Now, when you create an instance of Base, the vtable pointer of the instance will point to the vtable of Base. Likewise, an instance of Derived will have a pointer to Derived's vtable.

In these two vtables, there is only one entry for WhoAmI() in your example, the pointer in Base's vtable points to Base::WhoAmI() and the one in Derived's vtable points to Derived::WhoAmI().

So when you call WhoAmI() the runtime will lookup the vtable from the object and then the function pointer to the function that will be executed.

This said, it is obvious from the behaviour that you have seen, what a member function pointer is: nothing more or less than an offset into the vtable! In your case, this offset will most likely be simply zero, because WhoAmI() is the first and only entry in the vtable. When you call the function behind the member function pointer, you give it an object from which a vtable is looked up. And then the offset into the vtable (the member function pointer) is used to load a pointer to the actual code that gets executed, just like in any other call to a virtual function.

The only difference is, that in a normal virtual function call, the compiler will know the precise offset at which to look for the function pointer by the name of the function you call, when you use a member function pointer, that offset is supplied at runtime by the member function pointer.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • `it is obvious from the behaviour that you have seen, what a member function pointer is: nothing more or less than an offset into the vtable!` as written, this is false, given that such pointers work equally well for objects that don't have any vtable. for objects that _do_, it's an assumption about an implementation detail at best. – underscore_d Sep 06 '16 at 20:14
  • @underscore_d Ok, for a non-virtual member, the member function pointer obviously is just the same as a normal function pointer with the added ability to pass the hidden `this` argument. While you are right that compiler implementors need to think about this and implement it correctly, the interesting case is the virtual function. And it is also the case that this question is about. – cmaster - reinstate monica Sep 07 '16 at 21:13