C++之------回调函数

 

 

 一:What?(什么是回调函数)

 


 

  

   回调函数图文讲解

    

  

 

 

 

    谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。

 例如Win32下的窗口过程函数就是一个典型的回调函数。

           一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B叫甚名谁,所以S会约定B的接

口规范(函数原型),然后由C提前通过S的一个函数R告诉S自己将要使用B函数,这个过程称为回调函数的注册,R称为注册函数。

 

 

再看看回调函数的庐山面目

下面的SmartOS中Zegbe通讯函数回调的注册  (由于是公司商业代码,所以不贴逻辑代码)

 1 virtual void Register(TransportHandler handler, void* param = NULL)
 2     {
 3         if(handler)
 4         {
 5             _handler = handler;
 6             _param = param;
 7 
 8             if(!Opened) Open();
 9         }
10         else
11         {
12             _handler = NULL;
13             _param = NULL;
14         }
15     }
16 
17 protected:
18     virtual bool OnOpen() { return true; }
19     virtual void OnClose() { }
20     virtual bool OnWrite(const byte* buf, uint len) = 0;
21     virtual uint OnRead(byte* buf, uint len) = 0;
22 
23     // 是否有回调函数
24     bool HasHandler() { return _handler != NULL; }
25 
26     // 引发数据到达事件
27     virtual uint OnReceive(byte* buf, uint len)
28     {
29         if(_handler) return _handler(this, buf, len, _param);
30 
31         return 0;
32     }

    这个是整个通讯流类里面的回调函数使用

 

      下面举个例子说明回调函数的过程:

      ①:  比喻我打电话向你请教问题,当然是个难题      

      ②:  你一时也想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。   (注册回调函数)

      ③:  XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。   (回调函数操作)

 

          其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。

 

   回调函数的编程特点是:“异步+回调 ”

 

 

二   Why? ( 回调函数实现过程)


 

 

 

  1   
  2 
  3   1 //def.h   
  4   2 #include <iostream>   
  5   3 #include <stdio.h>   
  6   4 using namespace std;  
  7   5   
  8   6 typedef enum  
  9   7 {  
 10   8     CB_MOVE = 0,    //     
 11   9     CB_COMEBACK,    //   
 12  10     CB_BUYEQUIIP,   //   
 13  11 }cb_type;  
 14  12   
 15  13 typedef void(*cb_func)(void *);  
 16  14   
 17  15 class CCommu //模块类   
 18  16 {  
 19  17 public:  
 20  18     CCommu()  
 21  19     {  
 22  20         memset(func_list, 0, sizeof(cb_func) *(CB_BUYEQUIIP +1));  
 23  21         memset(func_args, 0, sizeof(void *) *(CB_BUYEQUIIP +1));  
 24  22     }  
 25  23   
 26  24     int reg_cb(cb_type type, cb_func func, void *args = NULL)//注册回调函数    
 27  25     {  
 28  26         if(type <= CB_BUYEQUIIP)   
 29  27         {  
 30  28             func_list[ type ] = func;  
 31  29             func_args[type] = args;  
 32  30             return 0;  
 33  31         }  
 34  32     }  
 35  33 public:  
 36  34     cb_func func_list[CB_BUYEQUIIP + 1] ;   //函数指针数组   
 37  35     void * func_args[CB_BUYEQUIIP +1];  
 38  36 };  
 39  37 
 40  38 
 41  39 
 42  40 
 43  41 //Gamestart.h
 44  42 #include "def.h"
 45  43 
 46  44 class CGameStart
 47  45 {
 48  46 
 49  47 public:
 50  48     CGameStart();
 51  49     ~CGameStart();
 52  50     void Init();
 53  51     void run();
 54  52     void Execute();
 55  53     
 56  54     //一些回调函数
 57  55     void static Move(void *args);
 58  56     void static Comeback(void *args);
 59  57     void static Buyequip(void *args);
 60  58 
 61  59 public:
 62  60     CCommu *pCommu;
 63  61 
 64  62 };
 65  63 
 66  64 
 67  65 
 68  66 //Gamestart.cpp   
 69  67 #include "Gamestart.h"   
 70  68   
 71  69 CGameStart::CGameStart():pCommu(NULL)  
 72  70 {}  
 73  71   
 74  72 void CGameStart::Init() //初始化的时候,注册回调函数    
 75  73 {  
 76  74     pCommu  = new CCommu;  
 77  75     pCommu ->reg_cb(CB_MOVE, Move , this);  
 78  76     pCommu->reg_cb (CB_COMEBACK, Comeback,this );  
 79  77 }  
 80  78   
 81  79 void CGameStart::run()  
 82  80 {  
 83  81     Init();  
 84  82 }  
 85  83   
 86  84 void CGameStart::Execute()  
 87  85 {  
 88  86     cout<<"callback funciton is running"<<endl;  
 89  87   
 90  88 }  
 91  89 CGameStart::~CGameStart()  
 92  90 {  
 93  91     if(pCommu != NULL)  
 94  92     {  
 95  93         delete pCommu;  
 96  94         pCommu = NULL;  
 97  95     }  
 98  96 }  
 99  97   
100  98 void CGameStart::Move(void *args)  
101  99 {  
102 100     CGameStart *pGame  = (CGameStart *)args;  
103 101     pGame -> Execute();  
104 102 }  
105 103 void CGameStart::Comeback(void *args)   
106 104 {  
107 105     //char *str = (char *)args;   
108 106     //cout << str <<endl;   
109 107 }  
110 108 
111 109 
112 110 
113 111 
114 112 
115 113 
116 114 //main.cpp
117 115 #include "Gamestart.h"   
118 116   
119 117 
120 118 int main()  
121 119 {  
122 120   
123 121     CGameStart *pGame = new CGameStart;  
124 122     pGame -> run();  
125 123     if(pGame->pCommu->func_list[CB_MOVE] != NULL)//回调函数的触发   
126 124     {  
127 125         pGame->pCommu->func_list[CB_MOVE](pGame->pCommu->func_args[CB_MOVE]);  
128 126     }  
129 127     return 0;  
130 128 }  

 

时代在不断进步,SDK不再是古老的API接口,C++面向对象编程被广泛的用到各种库中,因此回调机制也可以采用C++的一些特性来实现。
 通过前面的讲解,其实我们不难发现回调的本质便是:SDK定义出一套接口规范,应用程序按照规定实现它。这样一说是不是很简单,

想想我们C++中的继承,想想我们亲爱的抽象基类......于是,我们得到以下的代码:

 1 /// sdk.h
 2 #ifndef __SDK_H__
 3 #define __SDK_H__
 4 
 5 class Notifier // 回调类,应用程序需从此派生
 6 {
 7 public:
 8  virtual ~Notifier() { }
 9  virtual void got_answer(const char* answer) = 0; // 纯虚函数,用户必须实现它
10 };
11 
12 class Sdk // Sdk提供服务的类
13 {
14 public:
15  Sdk(Notifier* pnotifier); // 用户必须注册指向回调类的指针
16  void help_me(const char* question);
17 protected:
18  void do_it();  
19 protected:
20  Notifier* m_pnotifier; // 用于保存回调类的指针
21 };
22 
23 #define//__SDK_H__
24 
25 /// sdk.cpp
26 #include "sdk.h"
27 #include "windows.h"
28 #include <iostream>
29 using namespace std;
30 
31 Sdk::Sdk(Notifier* pnotifier) : m_pnotifier(pnotifier)
32 {
33 }
34 
35 void Sdk::help_me(const char* question)
36 {
37  cout << "help_me: " << question << endl;
38  do_it(); 
39 }
40 
41 void Sdk::do_it()
42 {
43  cout << "thinking..." << endl;
44  Sleep( 3000 );
45  cout << "think out." << endl;
46  cout << "call him." << endl;
47  m_pnotifier->got_answer( "2." );
48 }
49 
50 /// app.cpp
51 #include "sdk.h"
52 
53 class App : public Notifier // 应用程序实现一个从回调类派生的类
54 {
55 public:
56  App( const char* name ) : m_sdk(this), m_name(name) // 将this指针传入
57  {
58  }
59  void ask( const char* question )
60  {
61   m_sdk.help_me( question );
62  }
63  void got_answer( const char* answer ) // 实现纯虚接口
64  {
65   cout << m_name << " got_answer: " << answer << endl;
66  }
67 protected:
68  Sdk m_sdk;
69  const char* m_name;
70 };
71 
72 int main()
73 {
74  App app("ABC");
75  app.ask( "1+1=?");
76  return 0;
77 }

 

三: When?  (什么时候使用回调)


 

 

如果你是SDK的使用者,一旦别人制定了回调机制,那么你被迫得使用回调函数,因此这个问题只对SDK设计者有意义。
 从引入的目的看,回调大致分为三种:
 1) SDK有消息需要通知应用程序,比如定时器被触发;
 2) SDK的执行需要应用程序的参与,比如SDK需要你提供一种排序算法;
 3) SDK的操作比较费时,但又不能让应用程序阻塞在那里,于是采用异步方式,让调用函数及时返回,SDK另起线程在后台执行操作,待操作完成后再将结果通知应用程序。

 

END!

参考部分网络资源

 

 

欢迎大家一起交流 ,分享程序员励志故事。   幸福的程序员 QQ群: 

  

 

转载于:https://www.cnblogs.com/1hua1ye/p/3955651.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值