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群: 

  

时间: 2024-10-13 03:24:22

C++之------回调函数的相关文章

嵌入式&amp;iOS:回调函数(C)与block(OC)传 参/函数 对比

C的回调函数: callBack.h 1).声明一个doSomeThingCount函数,参数为一个(无返回值,1个int参数的)函数. void DSTCount(void(*CallBack)(int data_i32)); callBack.c 1).在doSomeThingCount函数,对运行次数自增,并调用参数--函数. void DSTCount(void(*CallBack)(int data_i32)) { static int numb = 0; numb++; (*Call

回调函数的本质,就是把函数当作参数(首先要定义函数类型)

//把一个方法当作另一个方法的参数, 就是回调方法, 大家习惯称作回调函数 type   TFunType = function(i: Integer): Integer; {声明一个方法类型} function MyFun(i: Integer): Integer;        {建立类型兼容的函数} begin   Result := i*2; end; {把函数当作参数, 再定义一个函数} function MyTest(x: Integer; F: TFunType): Integer

MFC 定时器 SetTimer 如何使用回调函数

创建工程名TestCallBack 自定义回调函数   定义为全局函数 在TestCallBackDlg.h文件开头定义 #pragma once void CALLBACK EXPORT TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime); 在TestCallBackDlg.cpp文件末尾实现函数 void CALLBACK EXPORT TimerProc(HWND hwnd,UINT message,UINT iTimerID

(转)回调函数

原文:http://blog.csdn.net/callmeback/article/details/4242260 其实回调就是一种利用函数指针进行函数调用的过程. 为什么要用回调呢?比如我要写一个子模块给你用,   来接收远程socket发来的命令.当我接收到命令后,   需要调用你的主模块的函数,   来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令,     我也不知道你的主模块是什么.cpp或者.h,   或者说,   我根本不用关心你在主模块里怎么处理它,   也不应该关心

ajax返回的值有两种方法,一种是把async:true改为false。 另一种是回调函数。

function load_val(callback){//定义一个回调函数 $.getJSON('test.php' , function(dat){ callback(data);//将返回结果当作参数返回 }); } load_val(function(data){ alert(data);//这里可以得到值 }); //否则的话你需要这样用同步ajax来实现了 function load_val2(){ var result; $.ajax({ dataType:'json', url

Python 3 进程池与回调函数

Python 3 进程池与回调函数 一.进程池 在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.多进程是实现并发的手段之一,需要注意的问题是: 很明显需要并发执行的任务通常要远大于核数 一个操作系统不可能无限开启进程,通常有几个核就开几个进程 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行) 例如当被操作对象数目不大时,可以直接利用multiprocessing中的Proces

【Cocos2d-x 3.0 基础系列一】 各类回调函数写法汇总

一.button回调 1. Lambda 表达式,C++11 Lambda 赋予了Cocos2d-x 3.0创建回调函数的灵活性. auto itemNor = Sprite::create("CloseNormal.png"); auto menuItem = MenuItemSprite::create(itemNor,nullptr,nullptr,[](Ref* sender) { log("show this msg."); }); auto menu =

IT小鲜肉 Widgets Tree 单选、多选、相关回调函数、获取选中的节点功能

写一个树控件并没有想象中的那么容易,今天又花了我一个1个多小时,主要为IT小鲜肉 Widgets Tree控件添加了 单选.多选.选择前和选择后两个回调函数.获取选中节点的功能.后面会继续努力完善这个树控件. 1.通过设置初始化时候的选项{select:true}开启单选,通过设置初始化时候的选项{select:{type:'multiple'}}开启多选 使用实例代码如下: 运行效果如下: 2.添加了onBeforeSelect回调函数,用来实现自定义选择,如果该函数返回false会中断默认的

设计一个函数,它接受不定数量的参数,这是参数都是函数。这些函数都接受一个回调函数作为参数,按照回调函数被调用的顺序返回函数名

function acceptFuncs() { var fnNames = []; //定义数组字面量,用来保存函数名称 for (var i = 0; i < arguments.length; i++) { //for循环检测接收到的每个参数是否为函数,是则传递回调函数给它,最后所结果压入数组中 if (typeof arguments[i] === "function") { fnNames.push(arguments[i](callback)); } } for (v

cocos2d-x学习笔记(c++与lua交互回调函数的处理)

本文假设读者已经会使用tolua++进行C++与lua之间的通讯 1.在头文件中定义注册回调函数,定义在MyClass类中 void register(unsigned short cmdID, LUA_FUNCTION func);//LUA_FUNCTION其实就是一个int void unregister(); 2.实现 void MyClass::register(unsigned short cmdID, LUA_FUNCTION func) { m_luaFunction = fun