Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1729366
  • 博文数量: 347
  • 博客积分: 9328
  • 博客等级: 中将
  • 技术积分: 2680
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-29 23:45
文章分类

全部博文(347)

文章存档

2016年(1)

2013年(4)

2012年(207)

2011年(85)

2010年(50)

分类: C/C++

2012-07-06 12:08:44

回调函数在C语言中用的非常频繁,它可以作用于结构体,但是到了C++中,类的成员函数是不能作为回调函数的。
详细说来,C的结构体中的函数都是函数指针,调用的时候一般不操作结构体内的数据否则要显式地在调用时传入
结构体的数据,即C中没有this这个概念。到了C++,假设类成员函数可以作为回调函数,那么这个函数对类成员
变量的操作作用于哪个实例呢?这显然是无法确定的,只有主动告知之。
关于this指针:看过C++类汇编代码的同志们都知道,在类实例调用它的成员函数时,call之前一定会有一步
  mov ecx, [ebp-xxx]
这个指令就是根据栈基址找到改实例在栈中的地址,并放入ecx中;在被调用的函数中,所有对类成员变量的操作
都是根据这个ecx带进来的地址完成的。因此,这就是类的成员函数不能作为回调函数的根本原因,那我们给他个
ecx不就行了嘛~
 

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdarg.h>

  4. template </*return value*/typename RETYPE>
  5. class Function{
  6. public:
  7.     Function(){
  8.         this->objaddr = 0;
  9.         this->callback = 0;
  10.     }

  11.     Function(int *objAddr, void *cb){
  12.         objaddr = (int)objAddr;
  13.         callback = cb;
  14.     }

  15.     Function(Function &input){
  16.         this->objaddr = input.objaddr;
  17.         this->callback = input.callback;
  18.     }

  19.     static RETYPE call(int addr, void *callback, int n){
  20.         __asm{MOV ECX, addr}
  21.         return ((int (__stdcall *)(int)) callback)(n);
  22.     }

  23.     //need to be extended
  24.     static Function bind(void *objAddr, ...){
  25.         va_list stack;
  26.         va_start(stack, objAddr);

  27.         Function ret((int *)objAddr, \
  28.             (void *)va_arg(stack, int));
  29.         return ret;
  30.     }

  31.     RETYPE operator()(int n){
  32.         int addr = objaddr;
  33.         void *cb = callback;
  34.         return call(addr, cb, n);
  35.     }
  36. private:
  37.     int objaddr;
  38.     void *callback;
  39. };


  40. class A{
  41. private:
  42.     Function<int> cb;
  43. public:
  44.     A(Function<int> callback)
  45.         :cb(callback)
  46.     {}
  47.     void t(int n){
  48.         cb(n);
  49.     }
  50. };

  51. class B{
  52. public:
  53.     int tt;
  54.     B():tt(0){
  55.         printf("B::tt inited as 0\n");
  56.     }
  57. //private:
  58.     int t(int n){
  59.         tt += n;
  60.         printf("B::tt changed to %d!\n", tt);
  61.         return tt;
  62.     }
  63. };

  64. int main(){
  65.     B b1, b2;

  66.     A a(Function<int>::bind(&b1, B::t));
  67.     a.t(2);
  68.     a.t(3);
  69.     
  70.     Function<int> fx;
  71.     fx = Function<int>::bind(&b2, B::t);
  72.     fx(9);
  73.     return 0;
  74. }
 
我定义了一个模板Function,它的作用有三点:
1、把类成员函数的地址取出来,并转换成void *类型(通过进出栈来退化thiscall约定);见 bind;
2、保存该回调函数作用的实例地址;
3、提供调用该回调函数的方式(转换成stdcall,之前没有注意这点而被默认的cdecl搞得莫名其妙,因为它没有自动清栈);见 call。
使用这个模板的方法很简单,它就是一个变量而已~
记得跟我培训的C++大神说过,类成员函数是不能作为线程被创建的,原因应该也是这个;当然,用这个模板的话迂回一下也是可以实现的。
阅读(1146) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~