致爱捣鼓虚表的童鞋

    下面这个例子,读起来可能有些晦涩,但是读懂了对于理解C++的函数调用约定函数指针动态绑定虚函数表 和 多态机制有极大的帮助。

    测试环境:Windows+vs,Linux+GCC

    成员函数在Windows下的默认的函数调用约定为__thiscall,这种约定会把this指针的值直接放到寄存器ECX中,而不是将其入参数栈。当采用常规的成员函数调用方式的时候,ECX会被填入正确的this值,但是我们通过普通的函数指针来调用成员函数的时候,ECX的值是未知的,故此采用__cdecl方式来将其编译。

#include <iostream>
 
class Base {
 public:
  Base() { std::cout << "Base()" << std::endl; }
  virtual ~Base() { std::cout << "~Base()" << std::endl; }
  virtual void
#ifdef _WIN32
      __cdecl
#endif
      echo(int i) { std::cout << "Base::echo(" << i << ")" << std::endl; }
};
 
class Derived : public Base {
 public:
  Derived() : Base() { std::cout << "Derived()" << std::endl; }
  virtual ~Derived() { std::cout << "~Derived()" << std::endl; }
  virtual void
#ifdef _WIN32
      __cdecl
#endif
      echo(int i) { std::cout << "Derived::echo(" << i << ")" << std::endl; }
};
  
int main(int argc, char* argv[]) {
  typedef void (
#ifdef _WIN32
      __cdecl
#endif
      *TFunction )(void*, int);


  Derived d;
  Base* p = &d;


  // On many platforms, std::size_t is synonymous with std::uintptr_t.
  size_t* vptr = reinterpret_cast<size_t*>(p);
  size_t* vtable = reinterpret_cast<size_t*>(*vptr);


#ifdef _WIN32
  TFunction invoker = reinterpret_cast<TFunction>(vtable[1]);
#else
  TFunction invoker = reinterpret_cast<TFunction>(vtable[2]);
#endif
  invoker(p, 1);
}

值得指出的是,C++标准上从来没有规定虚表指针放在对象的什么位置

因为这是个实现问题,与程序员对程序设计语言的使用没有任何关系。

尤其,对于爱捣鼓函数虚表和虚表指针的童鞋,捣鼓之前要知道这些个事情不能用到工程中,只限于加强学习理解。

我们得出的结论只限于某些平台(处理器体系结构&操作系统&编译器)。


参考资料:

1. X86体系的函数调用约定

2. std::uintptr_t 与 std::size_t 的关系


你可能感兴趣的:(C++,编译器)