准备知识
1.函数指针
函数名对应着函数存放在内存中的首地址,通过使用函数的首地址也可以调用函数。
函数指针即指向一个函数的指针,它的值只能是代码区中的一个地址。
函数指针定义格式如下:
类型标志符 (*函数指针名) (参数表) 例如 float (*pf) (float x);
例子:
#include <iostream.h>
int max(int x,int y)
{ int z;
if (x>y)
{
z=x;
}else{
z=y;
}
return (z);
}
void main()
{
int (*p)(int,int); /*定义函数指针变量,系统给分配4个字节的内存,用于存放函数入口地址*/
int a,b,c;
p=max; /*给函数指针变量赋值*/
cin>>a>>b;
c=(*p)(a,b); /*通过函数指针变量p调用所指向的函数*/
cout<<"a="<<a<<" b="<<b<<" max="<<c<<endl;
}
1)对函数指针赋值有两种方式:
p=&max;
p=max;
对于第二种方式,编译器会隐式的将max由 int () (int, int)类型转换成 int (*) (int, int).
2)使用函数指针变量调用函数可以有两种方式:
一种是和平时直接调用函数是一样的p(a,b)。
另一种是使用*对函数指针取值,从而实现对函数的调用,如上例所示(*p)(a,b)。
2.typedef+函数的用法
函数指针一般用于回调函数,当程序中有函数: void printHello(int i);
然后定义一个函数指针指向printHello,并调用这个函数:
void (*ptr) (int);
ptr = &printHello;
(*ptr)(3);
声明一个函数指针是比较复杂的,尤其是在多处地方声明同一个类型的函数指针变量时,代码更加复杂,所以简化方法
typedef void (*PrintHelloHandler) (int);
代码如下:
PrintHelloHandler pFunc;
pFunc = &printHello;
(*)pFun)c(3);
以后其他地方的程序需要声明类似的函数指针时,只需要下面代码:
PrintHelloHandler pFuncOther;
这样代码就变得更加简洁易懂。
参考:
https://jingyan.baidu.com/article/22a299b5d2179c9e19376a3a.html C/C++ typedef 用法
正题
3.回调函数
1)什么是回调函数
回调函数就是一个通过函数指针调用的函数。如果把函数指针(地址)通过参数的形式传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这事回调函数。回调函数不是由该函
数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或者条件进行响应。
2)回调函数的意义
因为可以把调用者和被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型的和限制条件(如返回值为int)的被调用函数。简而言之,回调函数就是允许用户把需要调
用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
3)回调函数的应用
回调函数在C语言中是通过函数指针来实现的,通过将回调函数的地址传给调用者从而实现回调。因此实现回调,首先必须定义函数指针
void Fun(char *s);//函数原型
void (*pFun) (char *);//函数指针
回调函数可以像普通函数一样被程序调用(如上边那个函数指针的例子),但只有它被当做参数传给调用者时才称作回调函数。
一个简单的回调函数的例子
#include <iostream>
using namespace std;
void test (int i)
{
cout<<"the num is "<<i<<"."<<endl;
}
void caller(int i, void (*ptr) (int))
{
(*ptr)(i);
}
int _tmain(int argc, _TCHAR* argv[])
{
caller(5, test);
return 0;
}
应用1
回调函数是不能显示调用的函数,通过将该回调函数的地址通过参数传递给调用者从而实现调用。回调函数的使用是必要的,在我们通过一个统一的接口实现不同的内容,这时用回调函数非常合适。
比如,我们为几个不同的设备写了不同的显示函数:void TVShow(); void ComputerShow(); void NoteBookShow()...等等。这时我们想用一个统一的显示函数,我们就可以用回调函数了。
void show( void (*ptr) () );
使用是根据所传入的参数不同而调用不同的回调函数。
应用2
C语言的标准库函数中很多地方都采用了回调函数让用户定制处理过程。如常用的快速排序、二分搜索函数等。
快速排序函数原型
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
应用:
struct PluginCfg
{
char name[32];
char value[128];
int reserved[16];
};
PluginCfg *m_cfgs;
void __cdecl qsort (void *base, size_t num, size_t width, int (__cdecl *comp)(const void *, const void *) )
{
char *lo, *hi; /* ends of sub-array currently sorting 数组的两端项指针,用来指明数组的上界和下界*/
char *mid; /* points to middle of subarray 数组的中间项指针*/
char *loguy, *higuy; /* traveling pointers for partition step 循环中的游动指针*/
size_t size; /* size of the sub-array 数组的大小*/
char *lostk[STKSIZ], *histk[STKSIZ];
int stkptr; /* stack for saving sub-array to be processed 栈顶指针*/
. . . . . .
lo = base;
. . . . . .
mid = lo + (size / 2) * width; /* find middle element */
/*第一项 中间项 和最后项三个元素排序*/
/* Sort the first, middle, last elements into order */
if (comp(lo, mid) > 0) {
swap(lo, mid, width);
}
if (comp(lo, hi) > 0) {
swap(lo, hi, width);
}
if (comp(mid, hi) > 0) {
swap(mid, hi, width);
}
. . . . . .
}
应用3
用C++写一个简单的回调函数:参考
http://www.cnblogs.com/kunhu/p/3713370.html C之回调函数
涉及到C++的回调函数,类成员函数指针等,不太理解,可参考
http://blog.youkuaiyun.com/eroswang/article/details/4153356 类的成员函数指针(比较深入)
http://blog.sina.com.cn/s/blog_50a0aa5e0100yhmx.html 指向成员函数的指针 typedef void (A::*PFUNC)(int)
http://www.cnblogs.com/xiongxuanwen/p/4290086.html C++类(Class)总结
class CTest;
typedef void (CTest::*DoMessageFunc)(char* msg, int msgid );
class CTest
{
public:
CTest(){}
~CTest(){}
void DoMsgFunc1(char* pMsg,int nID)
{
printf("%s\n",pMsg);
printf("回调函数\n");
}
void RegiestMsg(int nSrcID,DoMessageFunc pFunc)
{
m_pFunc = pFunc;
}
void HandleMessage(int nMsgID, char* pMsg, int nID)
{
(this->*m_pFunc)(pMsg,nID);
}
private:
DoMessageFunc m_pFunc;
};
using namespace std;
int main(int argc, char* argv[])
{
printf("Starting...... \n");
CTest obj ;
obj.RegiestMsg(12,&CTest::DoMsgFunc1);
obj.HandleMessage(1,"test",6);
printf("Ending...... \n");
return 0;
}
应用4
一个具体的例子,参考:
http://blog.chinaunix.net/uid-22488454-id-3057473.html
代码如下:
/*
* callback.h
*
* Created on: 2012-1-12
* Author: simondu
*/
#ifndef CALLBACK_H_
#define CALLBACK_H_
#include<stdlib.h>
#include<string.h>
#include <iostream>
using namespace std;
#define CALLBACKLIST_INIT_SIZE 10
#define CALLBACKLIST_INCREMENT 5
class CallBack;
typedef void *CallData;//回调数据指针类型定义
typedef void (CallBack::*CallBackFunction)(CallData); //指向回调成员函数的指针
typedef void (*CallBackStaticFunction)(CallData); //指向静态成员函数或普通函数的指针类型定义
class EventRecord{
private:
char *eventName; //回调事件名称
CallBack *pointerToCBO;//指向回调对象的指针
//指向成员函数的指针和指向静态成员函数(或普通函数)指针的共用体
union{
CallBackFunction pointerToCBF;
CallBackStaticFunction pointerToCBSF;
};
public:
EventRecord(void); //事件记录类的缺省构造函数
//构造包含成员函数的事件记录
EventRecord(char *ename,CallBack *pCBO,CallBackFunction pCBF);
//构造包含静态成员函数或普通函数的事件记录
EventRecord(char *ename,CallBackStaticFunction pCBSF);
~EventRecord(void);//析构事件记录
void operator = (const EventRecord& er);//重载赋值运算符
//判断当前事件记录的事件名是否为ename
int operator == (char *ename) const;
//判断当前事件记录是否和指定事件记录相等
int operator == (const EventRecord& er) const;
void Flush(void); //将当前事件记录清空
int IsEmpty(void) const;//判断事件记录是否为空(即事件名是否为空)
friend class CallBack; //让CallBack类能访问EventRecord的私有成员;
};
class CallBack {
private:
EventRecord *callBackList; //回调事件表
int curpos; //当前事件记录位置
int lastpos; //回调表中最后一空闲位置
int size; //回调表的大小
void MoveFirst(void) { curpos = 0; }//将当前记录置为第一条记录
void MoveNext(void) //将下一条记录置为当前记录
{
if(curpos == lastpos) return;
curpos++;
}
//判断回调表是否被遍历完
int EndOfList(void) const { return curpos == lastpos; }
public:
CallBack(void);//构造函数
CallBack(const CallBack& cb);//拷贝构造函数
~CallBack(void);//析构函数
void operator = (const CallBack& cb);// 重载赋值运算符
//将回调对象的成员函数、静态成员函数(或普通函数)
//注册为事件对象的回调函数
void AddCallBack(char *event,CallBackFunction cbf,CallBack *p);
void AddCallBack(char *event,CallBackStaticFunction cbsf);
//删除注册在指定事件上的回调函数
void RemoveCallBack(char *event,CallBackFunction cbf,CallBack *p);
void RemoveCallBack(char *event,CallBackStaticFunction cbsf);
void RemoveCallBack(char *event);// 删除某事件的全部记录
//执行注册在某一事件上的所有回调函数
void CallCallBack(char *event, CallData calldata = NULL);
};
#endif /* CALLBACK_H_ */
#include "stdafx.h"
/*
* callback.cpp
*
* Created on: 2012-1-12
* Author: simondu
*/
#include "callback.h"
EventRecord::EventRecord(void)
{
cout<<"EventRecord 构造。"<<endl;
eventName = NULL;
pointerToCBO = NULL;
//因为sizeof(CallBackFunction) > sizeof(CallBackStaticFunction)
pointerToCBF = NULL;
}
EventRecord::EventRecord(char *ename, CallBack *pCBO, CallBackFunction pCBF)
:pointerToCBO(pCBO), pointerToCBF(pCBF)
{
eventName = strdup(ename);
}
EventRecord::EventRecord(char *ename, CallBackStaticFunction pCBSF)
:pointerToCBO(NULL), pointerToCBSF(pCBSF)
{
eventName = strdup(ename);
}
EventRecord::~EventRecord(void)
{
if(eventName) delete eventName;
}
void EventRecord::operator = (const EventRecord& er)
{
if(er.eventName)
eventName = strdup(er.eventName);
else
eventName = NULL;
pointerToCBO = er.pointerToCBO;
pointerToCBF = er.pointerToCBF;
}
int EventRecord::operator == (char *ename) const
{
if((eventName == NULL)||ename == NULL)
return eventName == ename;
else
return strcmp(eventName,ename) == 0;
}
int EventRecord::operator == (const EventRecord& er) const
{
return (er == eventName) /*er和eventname不能交换位置*/
&&(pointerToCBO == er.pointerToCBO)
&&(pointerToCBO ?
(pointerToCBF == er.pointerToCBF):
(pointerToCBSF == er.pointerToCBSF));
}
void EventRecord::Flush(void)
{
if(eventName){
delete eventName;
eventName = NULL;
}
pointerToCBO = NULL;
pointerToCBF = NULL;
}
int EventRecord::IsEmpty(void) const
{
if(eventName == NULL)
return 1;
else
return 0;
}
//Callback类的实现
CallBack::CallBack(void)
{
cout<<"CallBack 构造。"<<endl;
//按初始尺寸为回调表分配内存空间
callBackList = new EventRecord[CALLBACKLIST_INIT_SIZE];
if(!callBackList){
cerr<<"CallBack: memory allocation error."<<endl;
exit(1);
}
size = CALLBACKLIST_INIT_SIZE;
lastpos = 0;
curpos = 0;
}
CallBack::CallBack(const CallBack& cb): curpos(cb.curpos),lastpos(cb.lastpos),size(cb.size)
{
callBackList = new EventRecord[size];
if(!callBackList){
cerr<<"CallBack: memory allocation error."<<endl;
exit(1);
}
//一一复制各条事件记录
for(int i = 0; i < size; i++) callBackList[i] = cb.callBackList[i];
}
void CallBack::operator = (const CallBack& cb)
{
curpos = cb.curpos;
lastpos = cb.lastpos;
size = cb.size;
delete [] callBackList;//删除旧的回调表
callBackList = new EventRecord[size];//重新分配内存空间
if(!callBackList){
cerr<<"CallBack: memory allocation error."<<endl;
exit(1);
}
//一一复制各条事件记录
for(int i = 0; i < size; i++) callBackList[i] = cb.callBackList[i];
}
CallBack::~CallBack(void)
{
delete [] callBackList;
}
void CallBack::AddCallBack(char *event, CallBackFunction pCBF, CallBack *pCBO)
{
//如事件名为空,退出
if( (event == NULL)?1:(strlen(event) == 0)) return;
//寻找因删除事件记录而产生的第一个空闲位置,并填写新事件记录
int start=0;
for(start=0; start < lastpos; start++ )
if(callBackList[start].IsEmpty()){
callBackList[start] = EventRecord(event,pCBO,pCBF);
break;
}
if(start < lastpos) return; //确实存在空闲位置
//没有空闲位置,在回调表后追加新记录
if(lastpos == size) //回调表已满,需“伸长”
{
EventRecord *tempList = callBackList;//暂存旧回调表指针
//以一定的步长“伸长”回调表
callBackList = new EventRecord[size + CALLBACKLIST_INCREMENT];
if(!callBackList){
cerr<<"CallBack: memory allocation error."<<endl;
exit(1);
}
//复制旧回调表中的记录
for(int i = 0; i < size; i++) callBackList[i] = tempList[i];
delete [] tempList;//删除旧回调表
size += CALLBACKLIST_INCREMENT;//记下新回调表的尺寸
}
//构造新的事件记录并将其填入回调表中
callBackList[lastpos] = EventRecord(event,pCBO,pCBF);
lastpos++;
}
void CallBack::AddCallBack(char *event,CallBackStaticFunction pCBSF)
{
if( (event == NULL)?1:(strlen(event) == 0)) return;
int start=0;
for(start = 0; start<lastpos; start++)
if(callBackList[start].IsEmpty()){
callBackList[start] = EventRecord(event,pCBSF);
break;
}
if(start < lastpos) return; //a hole is found
if(lastpos == size) //event list is insufficient
{
EventRecord *tempList = callBackList;
callBackList = new EventRecord[size + CALLBACKLIST_INCREMENT];
if(!callBackList){
cerr<<"CallBack: memory allocation error."<<endl;
exit(1);
}
for(int i = 0; i < size; i++) callBackList[i] = tempList[i];
delete [] tempList;
size += CALLBACKLIST_INCREMENT;
}
callBackList[lastpos] = EventRecord(event,pCBSF);
lastpos++;
}
//删除注册在指定事件上的成员函数
void CallBack::RemoveCallBack(char *event, CallBackFunction pCBF, CallBack *
pCBO)
{
if( (event == NULL)?1:(strlen(event) == 0)) return;
EventRecord er(event,pCBO,pCBF);
for(int i = 0; i < lastpos; i++)
if(callBackList[i] == er) callBackList[i].Flush();
}
//删除注册在指定事件上的静态成员函数或普通函数
void CallBack::RemoveCallBack(char *event,CallBackStaticFunction pCBSF)
{
if( (event == NULL)?1:(strlen(event) == 0)) return;
EventRecord er(event,pCBSF);
for(int i = 0; i < lastpos; i++)
if(callBackList[i] == er) callBackList[i].Flush();
}
//删除注册在指定事件上的所有回调函数
void CallBack::RemoveCallBack(char *event)
{
if( (event == NULL)?1:(strlen(event) == 0)) return;
for(int i = 0; i < lastpos; i++)
if(callBackList[i] == event) callBackList[i].Flush();
}
void CallBack::CallCallBack(char *event, CallData callData)
{
if( (event == NULL)?1:(strlen(event) == 0)) return;
CallBack *pCBO;
CallBackFunction pCBF;
CallBackStaticFunction pCBSF;
MoveFirst();
while(!EndOfList())
{
//如当前事件记录和指定事件不匹配,转入下一条记录继续循环
if(!(callBackList[curpos] == event))
{
MoveNext();
continue;
}
//如找到匹配记录
pCBO = callBackList[curpos].pointerToCBO;
//如事件记录中回调对象指针为空,说明该记录中保存的是静态函数指针
if(pCBO == NULL)
{
pCBSF = callBackList[curpos].pointerToCBSF;
pCBSF(callData);//调用该静态回调函数
}
else //如事件记录中回调对象指针非空,说明该记录中保存的是成员函数指针
{
pCBF = callBackList[curpos].pointerToCBF;
(pCBO->*pCBF)(callData);// 调用该回调对象的成员函数
}
MoveNext();
}
}
#include "stdafx.h"
/*
* test5.cpp
*
* Created on: 2012-1-11
* Author: simondu
*/
#include"callback.h"
class Speaker:public CallBack
{
private:
int volume;
public:
Speaker(int v): volume(v) {}
void IncreaseVolume(int v) //增加音量成员函数
{
volume += v;
if(volume > 20){
//“音量大于20”事件发生了
//调用注册在两事件上的回调函数
CallCallBack("音量改变了");
CallCallBack("音量大于20", &volume);
}
}
void DecreaseVolume(int v) //降低音量成员函数
{
volume -= v;
if(volume < 5){ //“音量小于5”事件发生了
//调用注册在两事件上的回调函数
CallCallBack("音量改变了");
CallCallBack("音量小于5", &volume);
}
}
};
//“耳朵”类
class Ear : public CallBack
{
public:
static void Response(CallData callData) //对“音量改变”的反应
{
cout<<"音量改变了."<<endl;
}
void HighVoiceResponse(CallData callData)//对高音的反应
{
cout<<"喂!太吵了!现在音量是:"<<*((int *)callData)<<endl;
}
void LowVoiceResponse(CallData callData)// 对低音的反应
{
cout<<"啊!我听不清了。现在音量是:"<<*((int *)callData)<<endl;
}
};
/*
* main.cpp
*
* Created on: 2011-12-1
* Author: simondu
*/
#include "test5.cpp"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
printf("Starting...... \n");
Speaker s(10); //现在音量为10
Ear e;
//为事件对象s注册回调函数
s.AddCallBack("音量大于20",(CallBackFunction)&Ear::HighVoiceResponse,&e);
s.AddCallBack("音量小于5",(CallBackFunction)&Ear::LowVoiceResponse,& e);
s.AddCallBack("音量改变了",(CallBackStaticFunction)&Ear::Response);
s.IncreaseVolume(12);//将音量增加12,现在音量位22
s.DecreaseVolume(20);//将音量减少20,现在音量位2
printf("Ending...... \n");
}
代码分析如下:
大概结构是这样的
class CallBack;
typedef void *CallData;//回调数据指针类型定义
typedef void (CallBack::*CallBackFunction)(CallData); //指向回调成员函数的指针
typedef void (*CallBackStaticFunction)(CallData); //指向静态成员函数或普通函数的指针类型定义
===========
类EventRecord
有成员变量
char *eventName; //回调事件名称
CallBack *pointerToCBO;//指向回调对象的指针
//指向成员函数的指针和指向静态成员函数(或普通函数)指针的共用体
union{
CallBackFunction pointerToCBF;
CallBackStaticFunction pointerToCBSF;
};
==========
类CallBack
有成员变量
EventRecord *callBackList; //回调事件表
int curpos; //当前事件记录位置
int lastpos; //回调表中最后一空闲位置
int size; //回调表的大小
成员函数:
//将回调对象的成员函数、静态成员函数(或普通函数)注册为事件对象的回调函数
void AddCallBack(char *event,CallBackFunction cbf,CallBack *p);
void AddCallBack(char *event,CallBackStaticFunction cbsf);
void CallCallBack(char *event, CallData calldata = NULL);//执行注册在某一事件上的所有回调函数
等等
============
class Speaker:public CallBack
成员函数
IncreaseVolume
DecreaseVolume
=============
class Ear : public CallBack
成员函数
Response
HighVoiceResponse
LowVoiceResponse
===============
主函数
=============================
主函数代码分析如下:
1.AddCallBack
第一个AddCallBack,传入event为“音量大于20”,并将类Ear的HighVoiceResponse函数地址作为参数传给了pCBF ,且将 Ear类的一个对象实例e的地
址传了进去。
通过传入的参数构造出一个EventRecord 对象,用传入的event名,和回调函数地址将事件名(“音量大于20”)和回调函数(HighVoiceResponse)
绑定起来。然后将构造出来的这个EventRecord 对象放入回调事件表callBackList中。后边的两个AddCallBack相当于分别将事件(“音量小于5”)和
回调函数(LowVoiceResponse)绑定,事件(“音量改变了”)和回调函数(Response)绑定。
2.IncreaseVolume
s.IncreaseVolume(12);
音量变成10+12==22大于20,所以调用callcallBack方法
callcallback("音量改变了");
会从回调事件表callBackList中找到所有event名为“音量改变了”的EventRecord,然后依次调用与之绑定的回调函数Response。
以上模式和duiversion中的消息响应模式很像
定义消息ID,和响应函数OnUpdateExpTime(和上例的
回调函数Responsed等对应),
然后绑定消息ID和响应函数OnUpdateExpTime(和上例中的
s.AddCallBack("音量大于20",(CallBackFunction)&Ear::HighVoiceResponse,&e);对应)。
当满足某条件调用AddDuiActionTask(...MSG_UPDATE_EXP_TIME..)向消息ID为MSG_UPDATE_EXP_TIME上发送消息(和上例的
IncreaseVolume后满足条件调用了 callcallback("音量改变了");向event名为“音量改变了”的Event发送消息),
然后就会从消息列表中拿出该消息并调用与之绑定的响应函数OnUpdateExpTime(和上例
中的
从回调事件表callBackList中找到event名为“音量改变了”的EventRecord,然后调用与之绑定的回调函数Response对应)。
待补充:回调函数和异步的关系。
C++的回调函数,类成员函数指针等