C ++函数指针。它的工作原理和原因如何?

时间:2014-07-26 07:05:22

标签: c++

考虑以下程序

#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可以针对thisTBase类提出不同的TDerived指针。

所以,第一个问题。

我读到函数指针更像是相对于this的偏移量。如果是这样,为什么我在(1)和(2) - 4197502中得到​​相同的函数指针值?

第二个问题

我在this函数(3)中输出CallF,它没问题。但后来我通过(this->*Func)();this)和函数TBase F调用函数this神奇地变得完全不同this(4) !它改变了它的类型和价值!怎么可能?编译器如何记住Func(哪种类型为typedef void (TBase::*TFunc)();)实际上来自TDerived类?编译器如何知道它应该在将this发送到F之前进行调整?为什么以及如何运作?

1 个答案:

答案 0 :(得分:1)

Itanium ABI中描述了一种可以实现的方法示例 - 如果您对如何实现C ++类模型感兴趣,我强烈建议您阅读整个文档。

当您从指向派生的成员函数转换为指向基本成员函数时,编译器知道当您调用它时this将指向未派生的基础。所以它需要确保它做的第一件事是将this从base *修改为派生*。它可以通过发明一个修改this并跳转到实际函数的填充函数,或者只是将偏移量与指针一起存储并在调用站点使用它来实现这一点(这是上面引用的参考) 。或者我确定还有其他方法。

static_cast不仅仅是一个编译时操作;通常它必须在运行时进行实际工作。)