让C++回调函数可以同时支持成员函数和静态函数的方法

回调函数是分层软件系统中经常使用的一种技巧,当下层需要调用上层的接口时,一般都使用回调函数来破除循环依赖。在纯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指针啦!

 

 
  1. #include <iostream>

  2. #include <functional>

  3. #include <list>

  4.  
  5. using namespace std;

  6.  
  7. // 通知结构体,包含通知的消息

  8. class timeup_notify_obj

  9. {

  10. public:

  11. int cur_time;

  12. };

  13.  
  14. // 函数对象基类,包含纯虚的operator()接口待子类实现。

  15. class timeup_notify_func : public unary_function<timeup_notify_obj, int>

  16. {

  17. public:

  18. virtual result_type operator()(const argument_type& notify_arg) const = 0;

  19. };

  20.  
  21. // 成员函数对象,其中保存有注册时的对象指针,以便调用。

  22. template<typename classname>

  23. class timeup_notify_memfunc : public timeup_notify_func

  24. {

  25. public:

  26. timeup_notify_memfunc(classname& in_callobj, result_type (classname::*in_func)(argument_type))

  27. : func(bind1st(mem_fun(in_func), &in_callobj))

  28. {

  29. }

  30. virtual result_type operator()(const argument_type& notify_arg) const

  31. {

  32. return func(notify_arg);

  33. }

  34. private:

  35. binder1st<mem_fun1_t<result_type, classname, argument_type> > func;

  36. };

  37.  
  38. // const成员函数对象,和成员函数对象唯一的区别是其this指针为const。

  39. template<typename classname>

  40. class timeup_notify_memfunc_const : public timeup_notify_func

  41. {

  42. public:

  43. timeup_notify_memfunc_const(const classname& in_callobj, result_type (classname::*in_func)(argument_type) const)

  44. : func(bind1st(mem_fun(in_func), &in_callobj))

  45. {

  46. }

  47. virtual result_type operator()(const argument_type& notify_arg) const

  48. {

  49. return func(notify_arg);

  50. }

  51. private:

  52. binder1st<const_mem_fun1_t<result_type, classname, argument_type> > func;

  53. };

  54.  
  55. // 全局/静态函数对象。

  56. class timeup_notify_static_func : public timeup_notify_func

  57. {

  58. public:

  59. timeup_notify_static_func(result_type (*in_func)(argument_type)) : func(ptr_fun(in_func))

  60. {

  61. }

  62. virtual result_type operator()(const argument_type& notify_arg) const

  63. {

  64. return func(notify_arg);

  65. }

  66. private:

  67. pointer_to_unary_function<argument_type, result_type> func;

  68. };

  69.  
  70. // 下面是一些辅助函数,用来生成对应类型的函数对象。

  71. template<typename classname>

  72. inline timeup_notify_memfunc<classname>* creat_timeup_func(classname& in_callobj, int(classname::*in_func)(timeup_notify_obj))

  73. {

  74. return new timeup_notify_memfunc<classname>(in_callobj, in_func);

  75. }

  76.  
  77. template<typename classname>

  78. inline timeup_notify_memfunc_const<classname>* creat_timeup_func(const classname& in_callobj,int (classname::*in_func)(timeup_notify_obj) const)

  79. {

  80. return new timeup_notify_memfunc_const<classname>(in_callobj, in_func);

  81. }

  82.  
  83. inline timeup_notify_static_func* creat_timeup_func(int (*in_func)(timeup_notify_obj))

  84. {

  85. return new timeup_notify_static_func(in_func);

  86. }

  87.  
  88. class BaseA

  89. {

  90. public:

  91. virtual void func_BaseA(){}

  92. };

  93.  
  94. class BaseB

  95. {

  96. public:

  97. virtual void func_BaseB(){}

  98. };

  99.  
  100. // 这里故意用一个多重继承,来体现其相对于void*指针会更安全。

  101. class A : public BaseA, public BaseB

  102. {

  103. public:

  104. virtual int update(timeup_notify_obj timeup_obj)

  105. {

  106. return timeup_obj.cur_time + my_time;

  107. }

  108.  
  109. int update_const(timeup_notify_obj timeup_obj) const

  110. {

  111. return timeup_obj.cur_time * my_time;

  112. }

  113.  
  114. static int update_static(timeup_notify_obj timeup_obj)

  115. {

  116. return timeup_obj.cur_time;

  117. }

  118.  
  119. int my_time;

  120. };

  121.  
  122. int update_global(timeup_notify_obj timeup_obj)

  123. {

  124. return timeup_obj.cur_time + 100;

  125. }

  126.  
  127. int main(int argc, _TCHAR* argv[])

  128. {

  129. list<timeup_notify_func*> notify_func_list;

  130. A a;

  131. a.my_time = 2;

  132. notify_func_list.push_back(creat_timeup_func(a, &A::update));

  133. A b;

  134. b.my_time = 5;

  135. notify_func_list.push_back(creat_timeup_func(b, &A::update_const));

  136. notify_func_list.push_back(creat_timeup_func(A::update_static));

  137. notify_func_list.push_back(creat_timeup_func(update_global));

  138.  
  139. timeup_notify_obj timeup;

  140. timeup.cur_time = 4;

  141.  
  142. // 循环遍历调用所有类型的函数

  143. for (list<timeup_notify_func*>::iterator it = notify_func_list.begin(); it !=notify_func_list.end(); ++it)

  144. {

  145. int ret = (*(*it))(timeup);

  146. cout << ret << endl;

  147. }

  148.  
  149. return 0;

  150. }

代码运行结果:

 

6
20
4
104

 

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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值