简体   繁体   中英

C++ Function Pointers. How and why it works?

Consider following programme

#include <iostream>
#include <typeinfo>

template <class T>
void Output(const char * Str, T Func)
   {
      void *Ptr = reinterpret_cast<void *>(Func);
      std::ptrdiff_t Num = reinterpret_cast<std::ptrdiff_t>(Ptr);
      std::cout << Str << " " << Num << std::endl;
   }

class TAnotherBase  { long a[10]; };

struct TBase {
      typedef void (TBase::*TFunc)();
      TFunc Func;
      TBase(TFunc F) {
            Func = F;
            Output("Ctor TBase ", Func);
         }
      void CallF() {
            std::cout << "This in TBase: " << typeid(this).name() << " " << this << std::endl;
            (this->*Func)();
         }
   };

struct TDerived: public TAnotherBase, public TBase {
      TDerived(): TBase(static_cast<TBase::TFunc>(&TDerived::F)) {
            Output("Ctor TDerived ", &TDerived::F);
            CallF();
         }
      void F() {
            std::cout << "This in TDerived::F: " <<typeid(this).name() << " " << this << std::endl;
         }
   };

int main(int argc, char **argv) {
      TDerived Derived;
      return 0;
   }

It generates this output:

Ctor TBase  4197502                                (1)
Ctor TDerived  4197502                             (2)
This in base: P5TBase 0x7fff6b30fc00               (3)
This in TDerived::F: P8TDerived 0x7fff6b30fbb0     (4)

What is going on here

I have function F in TDerived class, then I send pointer to the function to TBase class: TDerived(): TBase(static_cast<TBase::TFunc>(&TDerived::F)) { and (1) output function pointer. Then I output function pointer in TDerived class (2) and make TBase class to call the function: `CallF(); (4), (5).

TAnotherBase is here to make different this pointers of TBase and TDerived classes.

So, first question.

I read that function pointers is more like offsets relative to this . If so, why I get the same function pointer values in (1) and (2) - 4197502 ?

Second question

I output this in CallF function (3) and it is all right. But then I call function (this->*Func)(); via this (which is TBase ) and in function F this magically becomes completely different this (4)! It changes its type and value! How it is possible? How compiler still remembers that Func (which type is typedef void (TBase::*TFunc)(); ) is actually from TDerived class? How compiler knows that it should adjust this before sending it to F ? Why and how it works?

An example of one way this can be done is described in the Itanium ABI - if you're interested in how the C++ class model can be implemented I highly recommend reading that whole document.

When you convert from a pointer-to-derived-member-function to pointer-to-base-member-function the compiler knows that when you call it this will point to base not derived. So it needs to make sure that the first thing it does is to modify this back from base* to derived*. It can either do this by inventing a shim function which modifies this and jumps to the real function, or by just storing the offset along with the pointer and using it at the call site (which is what the reference above describes). Or I'm sure there are other ways.

( static_cast is not just a compile time operation; quite often it has to do real work at runtime.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM