回调函数在C语言中用的非常频繁,它可以作用于结构体,但是到了C++中,类的成员函数是不能作为回调函数的。
详细说来,C的结构体中的函数都是函数指针,调用的时候一般不操作结构体内的数据否则要显式地在调用时传入
结构体的数据,即C中没有this这个概念。到了C++,假设类成员函数可以作为回调函数,那么这个函数对类成员
变量的操作作用于哪个实例呢?这显然是无法确定的,只有主动告知之。
关于this指针:看过C++类汇编代码的同志们都知道,在类实例调用它的成员函数时,call之前一定会有一步
mov ecx, [ebp-xxx]
这个指令就是根据栈基址找到改实例在栈中的地址,并放入ecx中;在被调用的函数中,所有对类成员变量的操作
都是根据这个ecx带进来的地址完成的。因此,这就是类的成员函数不能作为回调函数的根本原因,那我们给他个
ecx不就行了嘛~
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
template </*return value*/typename RETYPE>
class Function{
public:
Function(){
this->objaddr = 0;
this->callback = 0;
}
Function(int *objAddr, void *cb){
objaddr = (int)objAddr;
callback = cb;
}
Function(Function &input){
this->objaddr = input.objaddr;
this->callback = input.callback;
}
static RETYPE call(int addr, void *callback, int n){
__asm{MOV ECX, addr}
return ((int (__stdcall *)(int)) callback)(n);
}
//need to be extended
static Function bind(void *objAddr, ...){
va_list stack;
va_start(stack, objAddr);
Function ret((int *)objAddr, \
(void *)va_arg(stack, int));
return ret;
}
RETYPE operator()(int n){
int addr = objaddr;
void *cb = callback;
return call(addr, cb, n);
}
private:
int objaddr;
void *callback;
};
class A{
private:
Function<int> cb;
public:
A(Function<int> callback)
:cb(callback)
{}
void t(int n){
cb(n);
}
};
class B{
public:
int tt;
B():tt(0){
printf("B::tt inited as 0\n");
}
//private:
int t(int n){
tt += n;
printf("B::tt changed to %d!\n", tt);
return tt;
}
};
int main(){
B b1, b2;
A a(Function<int>::bind(&b1, B::t));
a.t(2);
a.t(3);
Function<int> fx;
fx = Function<int>::bind(&b2, B::t);
fx(9);
return 0;
}
我定义了一个模板Function,它的作用有三点:
1、把类成员函数的地址取出来,并转换成void *类型(通过进出栈来退化thiscall约定);见 bind;
2、保存该回调函数作用的实例地址;
3、提供调用该回调函数的方式(转换成stdcall,之前没有注意这点而被默认的cdecl搞得莫名其妙,因为它没有自动清栈);见 call。
使用这个模板的方法很简单,它就是一个变量而已~
记得跟我培训的C++大神说过,类成员函数是不能作为线程被创建的,原因应该也是这个;当然,用这个模板的话迂回一下也是可以实现的。