让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;}
代码运行结果:

6
20
4
104

上面这个例子中的timeup_notify_func函数对象其实具有很强的扩展性,你可以轻松的把它做成一个支持所有类型事件的模板,而不仅仅是只能用于timeup事件。只要把timeup_notify_obj这个事件类型给参数化就可以了。

0 0
原创粉丝点击