魔法王座神格在哪开启:C++中实现类回调的办法

来源:百度文库 编辑:九乡新闻网 时间:2024/07/14 02:08:26
当我们使用普通函数做为回调函数时, 一般格式如下: 
typedef int (*pFunc) (int , int) ; 
但是如果要使用类成员函数做为回调函数时, 我们想到的格式会是下面的样子: 
typedef int (*CClassName::Func) (int, int) ; 
如是经过类型检查时, 会如愿收到如下的错误: 
error C2350: 'CClassName::Func' is not a static member 
这是因为普通成员函数和静态成员函数在内存中的位置是不一样的. 普通成员函数在代码区该类的代码区间中 , 而静态成员函数则和普通函数一样存储在数据区, 只是比普通函数多了一个作用域区分符而已. 问题就是: 有办法使用普通成员函数做为回调函数使用吗? 
解决办法: 
答案是肯定的. 但是要做到这点必须先解决两个问题: 一是如何获取函数的地址并传递到无类型指针中去,或者其他类型指针. 主要是要去掉作用域区分符.二是获取了函数地址之后如何调用函数. 
第一个问题的解决中间运用了编译器的一个技巧. 我们知道所有的转换或者匹配必须经过类型检查, 除了使用可变参数的函数之外. 这样我们就可以把 CClassName::Func 以可变参数的形式转换成指针, 从而获取除去类对象地址之后的函数偏移地址. 
第二个问题问题就是如何调用函数的问题. 我们知道所有类对象都隐藏了一个指象对象自己的this指针. 只要我们把类对象的地址传递过来, 我们就有了对象的基址, 加上上面获得的偏移就可以完全访问类成员函数了. 我们知道一般函数的调用约定都属于 __stacall, 这种调用约定是先将调用者入栈,然后是地址, 然后是参数从右至左依次入栈. 这样我们使用一段汇编代码就可以手动完成函数的调用了. 将类对象指针存入ECX寄存器, 然后依次压入参数. 
关键代码: 
第一个问题的解决代码如下: 
void* OffsetToVoid( void* Dummy, ... ) 

va_list list; 
void* RetVal; 

va_start( list, Dummy ); 
RetVal = va_arg( list, void* ); 
va_end( list ); 

return RetVal; 


将函数偏移地址以可变参数的形式传递进来, 避过类型检查. 



第二个问题的解决代码: 

int Call( void* pObj, 
void* pOffset, 
int arg1, 
int arg2 ) 

__asm 

mov ecx,[pObj]; 
push arg2; 
push arrg1; 
call [pOffset]; 



将参数压栈, 并调用函数 . 



使用方法: 

CClassName obj; 

int nRet; 

nRet = Call( &obj, 

OffsetToVoid(0, CClassName::Func) 

value1, 

value2); 


PS: 类成员函数也必须满足__stdcall(Pascall)式调用约定.  附件中为源代码。