成员函数指针【转贴】

来源:互联网 发布:淘宝店铺裤子类标志 编辑:程序博客网 时间:2024/04/30 02:07

#include "stdio.h"
class A
{
   public:
             virtual void t(){printf("base/n");};
             virtual void t2(){};
             int rt()
             {
             printf("non virtual func/n");
             return 1;
             };

};
class B:public A
{
          public:
               virtual void t(){ printf("derive/n");};
               virtual void t2(){};
};
void justfun()
{
  int a;
  a=0;
};

int main()
{
        A *p;
        B O;
        p=&O;
        int(*cast)();
        void(*nof)();
        void(A::*pmf)();
        int(A::*rpmf)();
        nof=&justfun;
        rpmf=&A::rt;
        pmf=&A::t2;
        // cast=(int * ())(rpmf);
        __asm
        {
        push eax
        mov eax,rpmf
        mov cast,eax
        pop eax
         }
printf("%x %x %x/n",nof,pmf,rpmf);
(*cast)();
(*nof)();
(p->*rpmf)();
(O.*pmf)();
(p->*pmf)();
pmf=&A::t;
(p->*pmf)();
p->t2();
return 0;
}
今天早上看到一个朋友的留言,关于通过成员函数指针调用成员函数的问题。
成员函数指针调用跟普通函数指针调用不同。
因为成员函数有虚函数和非虚函数的差别,调用的时候,跟普通非成员函数调用有不同。
对于非成员函数,取函数名的地址,得到的就是该函数内存中的起始地址,然后直接调用就可以。在汇编层面上,就是直接call addr.
对于非虚成员函数,取得的地址其实也就是完整的函数地址。可以直接调用。但是因为类型的原因,不能把成员函数的地址直接给一个普通的函数指针。成员函数指针的类型是classname::ptr的形式,而且是thiscall,thiscall是c++保 留关键字,所以无法直接给一个普通的函数指针,用强制转换也不行。因为c++不允许对函数指针类型做强制转换,所以我在上面的代码中用嵌入汇编,完成强制转换,通过调用,发现上面的理解是正确的。
对于虚成员函数就比较麻烦。如果不用指针调用,编译器就直接把object的地址放到ecx中,其实也就是vtable的地址,然后在这个vtable上就可以找到正确的函数。如果通过指针调用,编译器无法保证这个指针以后会不会发生变化,也就是说这个指 针变量,以后可能会指向这个class的其他成员函数,所以编译器一旦检测到这种&classname::funcname的时候,就会生成一个vcall,然后把vcall的地址作为&classname::funcname的结果返回。vcall其实指向的是一组代码,大概如下形式:
mov eax,[ecx]
call [eax+n]
其中n是根据不同funcname在vtable中不同偏移决定的。前面说过,如果不通过指针调用,编译器可以根据函数名字找到偏移,但是通过指针,编译器就找不到名字了,找到的是一个地址,所以必须在地址上强制生成这个n.所以代码的作用就是在vtab le,也就是[ecx]中,找到正确的函数。
上面的结论是我调试出来的,大家可以在中间放断点,通过alt+8查看汇编代码,得到结论。
同时c++标准没有规定编译器实现细节,上面的东西只对vc6 debug模式下有说明作用。

原创粉丝点击