考虑以下程序
#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;
}
它会生成此输出:
Ctor TBase 4197502 (1)
Ctor TDerived 4197502 (2)
This in base: P5TBase 0x7fff6b30fc00 (3)
This in TDerived::F: P8TDerived 0x7fff6b30fbb0 (4)
这里发生了什么
我在F
类中有函数TDerived
,然后我将指向函数的指针发送到TBase
类:TDerived(): TBase(static_cast<TBase::TFunc>(&TDerived::F)) {
和(1)输出函数指针。
然后我在TDerived
class(2)中输出函数指针并使TBase
类调用函数:`CallF(); (4),(5)。
TAnotherBase
可以针对this
和TBase
类提出不同的TDerived
指针。
所以,第一个问题。
我读到函数指针更像是相对于this
的偏移量。如果是这样,为什么我在(1)和(2) - 4197502中得到相同的函数指针值?
第二个问题
我在this
函数(3)中输出CallF
,它没问题。但后来我通过(this->*Func)();
(this
)和函数TBase
F
调用函数this
神奇地变得完全不同this
(4) !它改变了它的类型和价值!怎么可能?编译器如何记住Func
(哪种类型为typedef void (TBase::*TFunc)();
)实际上来自TDerived
类?编译器如何知道它应该在将this
发送到F
之前进行调整?为什么以及如何运作?
答案 0 :(得分:1)
Itanium ABI中描述了一种可以实现的方法示例 - 如果您对如何实现C ++类模型感兴趣,我强烈建议您阅读整个文档。
当您从指向派生的成员函数转换为指向基本成员函数时,编译器知道当您调用它时this
将指向未派生的基础。所以它需要确保它做的第一件事是将this
从base *修改为派生*。它可以通过发明一个修改this
并跳转到实际函数的填充函数,或者只是将偏移量与指针一起存储并在调用站点使用它来实现这一点(这是上面引用的参考) 。或者我确定还有其他方法。
(static_cast
不仅仅是一个编译时操作;通常它必须在运行时进行实际工作。)