让C++回调函数可以同时支持成员函数和静态函数的方法
来源:互联网 发布:电脑打字软件不见了 编辑:程序博客网 时间:2024/06/07 09:56
回调函数是分层软件系统中经常使用的一种技巧,当下层需要调用上层的接口时,一般都使用回调函数来破除循环依赖。在纯C语言环境中,回调函数用起来很简单——一个函数指针而已。但是当大家都用上C++以后,回调函数碰到了点麻烦:很多模块接口都是成员函数,怎么回调?
一些较有经验的程序员会说:成员函数也是可以取函数指针的。没错,但是如果当一个事件触发时,需要依次调用很多个模块的接口呢?(这个场景在“观察者”设计模式中是很常见的)你还能用成员指针么?为了解决这个问题,有人把this指针作为回调函数的一个参数传递,但需要把指针强制转换为void*类型——这种操作一旦碰上多重继承会产生令人头疼的问题(参见我的另一篇博客:《万恶的void*指针类型转换》,我向来都反对滥用void指针)。还有人把回调的函数写成类静态成员函数——这在操作类内部数据上存在诸多不便。
那有没有完美的解决办法?C++有个强大的工具——模板,可以用来简化这个问题。不管是成员函数、静态成员函数、全局函数,只要它们的参数相同,我们都可以看成是一类函数指针。有人很快会产生疑问:成员函数事实上多一个this指针参数,它和全局函数参数个数是不同的。没错,但别忘了STL中有个东东叫函数适配器,它可以把2个参数的函数改造成1个参数。例如:
bind1st(less<int>(), 10));
less<int>是一个二元函数,用来判断第一个参数是否比第二个小。bind1st会生成一个一元函数,让参数x进行less<int>(10, x)的调用。就像它的名字一样,bind1st会把第一个参数“绑死”,从而让二元函数变身为一元函数。
有了这个工具,我们就可以把成员函数的第一个参数——this指针“绑死”,从而也把它改造成和全局/静态函数一样了!
但接下来还有个技术问题:要依次的调用各种函数,就需要用一个容器把这些函数对象给装起来,需要所有这些函数有共同的基类。虽然STL中确实有这种基类——unary_function,但这个类并没有合适的虚函数可以让子类执行操作。所以我们还必须自己定义一个基类来将operator()处理成虚函数。
下面我就给一个例子,在这段代码中,多个模块都可以注册一个函数指针来响应timeup事件。当timeup事件发生时,通知者会循环遍历调用所有需要响应的函数,无论你是成员函数、const成员函数、静态成员函数、全局函数……全部都可以一视同仁的接收同样的通知!小伙伴们再也不用操心该死的this指针啦!
#include <iostream>#include <functional>#include <list>using namespace std;// 通知结构体,包含通知的消息class timeup_notify_obj{public: int cur_time;};// 函数对象基类,包含纯虚的operator()接口待子类实现。class timeup_notify_func : public unary_function<timeup_notify_obj, int>{public: virtual result_type operator()(const argument_type& notify_arg) const = 0;};// 成员函数对象,其中保存有注册时的对象指针,以便调用。template<typename classname>class timeup_notify_memfunc : public timeup_notify_func{public: timeup_notify_memfunc(classname& in_callobj, result_type (classname::*in_func)(argument_type)) : func(bind1st(mem_fun(in_func), &in_callobj)) { } virtual result_type operator()(const argument_type& notify_arg) const { return func(notify_arg); }private: binder1st<mem_fun1_t<result_type, classname, argument_type> > func;};// const成员函数对象,和成员函数对象唯一的区别是其this指针为const。template<typename classname>class timeup_notify_memfunc_const : public timeup_notify_func{public: timeup_notify_memfunc_const(const classname& in_callobj, result_type (classname::*in_func)(argument_type) const) : func(bind1st(mem_fun(in_func), &in_callobj)) { } virtual result_type operator()(const argument_type& notify_arg) const { return func(notify_arg); }private: binder1st<const_mem_fun1_t<result_type, classname, argument_type> > func;};// 全局/静态函数对象。class timeup_notify_static_func : public timeup_notify_func{public: timeup_notify_static_func(result_type (*in_func)(argument_type)) : func(ptr_fun(in_func)) { } virtual result_type operator()(const argument_type& notify_arg) const { return func(notify_arg); }private: pointer_to_unary_function<argument_type, result_type> func;};// 下面是一些辅助函数,用来生成对应类型的函数对象。template<typename classname>inline timeup_notify_memfunc<classname>* creat_timeup_func(classname& in_callobj, int(classname::*in_func)(timeup_notify_obj)){ return new timeup_notify_memfunc<classname>(in_callobj, in_func);}template<typename classname>inline timeup_notify_memfunc_const<classname>* creat_timeup_func(const classname& in_callobj,int (classname::*in_func)(timeup_notify_obj) const){ return new timeup_notify_memfunc_const<classname>(in_callobj, in_func);}inline timeup_notify_static_func* creat_timeup_func(int (*in_func)(timeup_notify_obj)){ return new timeup_notify_static_func(in_func);}class BaseA{public: virtual void func_BaseA(){}};class BaseB{public: virtual void func_BaseB(){}};// 这里故意用一个多重继承,来体现其相对于void*指针会更安全。class A : public BaseA, public BaseB{public: virtual int update(timeup_notify_obj timeup_obj) { return timeup_obj.cur_time + my_time; } int update_const(timeup_notify_obj timeup_obj) const { return timeup_obj.cur_time * my_time; } static int update_static(timeup_notify_obj timeup_obj) { return timeup_obj.cur_time; } int my_time;};int update_global(timeup_notify_obj timeup_obj){ return timeup_obj.cur_time + 100;}int main(int argc, _TCHAR* argv[]){ list<timeup_notify_func*> notify_func_list; A a; a.my_time = 2; notify_func_list.push_back(creat_timeup_func(a, &A::update)); A b; b.my_time = 5; notify_func_list.push_back(creat_timeup_func(b, &A::update_const)); notify_func_list.push_back(creat_timeup_func(A::update_static)); notify_func_list.push_back(creat_timeup_func(update_global)); timeup_notify_obj timeup; timeup.cur_time = 4; // 循环遍历调用所有类型的函数 for (list<timeup_notify_func*>::iterator it = notify_func_list.begin(); it !=notify_func_list.end(); ++it) { int ret = (*(*it))(timeup); cout << ret << endl; } return 0;}代码运行结果:
20
4
104
上面这个例子中的timeup_notify_func函数对象其实具有很强的扩展性,你可以轻松的把它做成一个支持所有类型事件的模板,而不仅仅是只能用于timeup事件。只要把timeup_notify_obj这个事件类型给参数化就可以了。
- 让C++回调函数可以同时支持成员函数和静态函数的方法
- 回调函数的使用封装(支持全局函数,类静态函数,类成员函数)
- 让成员函数可以成为回调函数
- 类的成员函数是否可以做为回调函数?
- 如何让类的成员函数作为回调函数
- 如何让类的成员函数作为回调函数
- 如何让类的成员函数作为回调函数
- 回调函数的C++ 封装(非静态成员函数的回调函数实现方法)
- 类的成员函数作为回调函数的方法
- 让静态成员函数具有多态性(浅谈如何使回调函数具有多态性)
- 回调函数调用类成员函数的方法 -转贴
- 回调函数调用类成员函数的方法
- 回调函数调用类成员函数的方法
- 回调函数调用类成员函数的方法
- 回调函数调用类成员函数的方法
- 回调函数调用类成员函数的方法
- 利用Thunk让C++成员函数变回调函数
- 利用Thunk让C++成员函数变回调函数
- 对灰度图像进行互补运算.m
- 万恶的void*指针类型转换
- 二进制图像求补.m
- 放大图片.m
- 非线性变换.m
- 让C++回调函数可以同时支持成员函数和静态函数的方法
- 如何用u盘制作linux和windows公用启动盘
- 腐蚀操作.m
- 哈夫曼编码.m
- 哈夫曼编码的MATLAB实现(基于0、1编码):
- 使用STL导致.o增大的真相
- 哈夫曼图片编码.m
- 花样swap函数
- 函数分布的快速绘图.m