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.