副标题#e#
用过C++举办过面向工具措施设计的用户都知道,措施中的工具很少单独存在。不思量工具间的彼此浸染险些是不行能的。所以,标识工具间的干系或成立工具间的动静毗连是面向工具措施设计的一项重要任务。本文着重从C++措施设计的角度,提出一种成立工具间动静毗连的实用要领。假如你想具体相识面向工具措施设计技能,请参阅有关专著。各人都知道工具是数据和要领的封装体。在C++中,它们别离表示为数据成员和成员函数。措施设计者通过执行工具的各类要领,来改变工具的状态(即改变工具的属性数据)。从而使该工具产生某些“事件”。当一工具产生某事件时,它凡是需向其它相关工具发送“动静”,请求它们作出一些处理惩罚。 这时,产闹事件并向其它工具请求处理惩罚的工具被称为“事件工具”,而处理惩罚事件的工具被称为“回调工具”。回调工具对事件的处理惩罚称为“回调函数”。在C++中,这一进程相当于:当事件工具产闹事件时,挪用回调工具的某些成员函数。凡是的作法是回调工具向事件工具通报工具指针。但这种要领不通用。为了淘汰措施设计的事情量,本文提出一种成立工具间动静毗连的系统要领。它的思路是:将“事件产生→请求处理惩罚→执行处理惩罚”这一进程抽象成一个“回调”(CallBack)类。通过担任,用户可以轻松获取成立工具间动静毗连的机制。
一、回调类的数据布局及其成员函数
本文提出的CallBack类支持三种回调函数。它们是:回调工具中的成员函数,属于回调类的静态成员函数和普通的C函数。CallBackle类中包括一回调函数表callBackList。它用于记录事件名称,指向回调函数及回调工具的指针。该表的每一个节点为一个事件记录EventRecord。每个事件记录包括三个域:事件名指针eventName,指向回调工具的指针pointerToCBO,指向回调函数的指针pointerToCBF或pointerToCBSF(个中,pointerToCBF指向回调工具的成员函数,pointerToCBSF指向回调类的静态成员函数或普通函数。它们同处于一共用体内)。CallBack类所提供的回调机制是这样的:在事件工具上注册回调工具中的回调函数;当事件产生时,事件工具在其回调表中检索并执行回调函数。从而使二者的动静毗连得以成立。(关于该类的详细实现,请参阅文后所附的措施清单)
#p#副标题#e#
回调工具
事件工具
事件名 回调工具指针 回调函数指针
“event” pointerCBO pointerToCBF或
pointerTOCBSF
– – – – – –
AddCallBack: 注册事件名和指向回调函数,回调工具的指针
CallCallBack: 在回调表中,检索注册在指定事件上回调函数并挪用它们
事件产生时,挪用CallCallBack函数
对事件event举办处理惩罚的成员函数
从CallBack类担任的回调表callBackList, 成员函数AddCallBack和CallCallBack。
当回调函数为静态成员函数或普通C函数时, pointerToCBO为NULL。
事件名是回调表callBackLis中的检索要害字。
回调工具中其它成员函数
CallBack类的成员函数AddCallBack用来将回调函数注册到事件工具的回调表中。它有两个重载版本:
void CallBack::AddCallBack(char *event,CallBackFunction cbf,CallBack *p);
void CallBack::AddCallBack(char *event,CallBackStaticFunction cbsf);
个中,第一个AddCallBack用来将某回调工具的成员函数注册到事件工具的回调表中。第二个AddCallBack用来将或某回调类的静态成员函数注册到事件工具的回调表中。在上参数表中,event是指向事件名字符串的指针,p是指向回调工具的指针,cbf和cbsf别离是指向成员函数及静态成员函数(或普通函数)的指针。当回调函数来自某回调工具SomeObject时,通报成员函数指针应回收如下名目:(CallBackFunction)&SomeObject::MemberFunctionName; 通报SomeObject类的某静态成员函数指针应回收名目:(CallBackStaticFunction)& SomeObject::FunctionName;通报措施中普通函数指针时,只需通报函数名即可。
CallBack类的成员函数void CallBack::CallCallBack(char *ename, CallData calldata = NULL)用来挪用注册在事件ename上的所有回调函数。个中,calldata为数据指针(CallData实际上就是void*,详见措施清单)。事件工具可通过它向回调工具通报有用的数据。该成员函数凡是在事件工具的成员函数中挪用,因为凡是只有事件工具的成员函数才气改变工具的内部数据,从而使某些事件产生。
成员函数RemoveCallback用来删除注册在事件工具上的回调函数。它的三个重载版本依次为:
void CallBack::RemoveCallBack(char *event,CallBackFunction cbf,CallBack *p);
void CallBack::RemoveCallBack(char *event,CallBackStaticFunction cbsf);
void CallBack::RemoveCallBack(char *event);
#p#分页标题#e#
个中,event,cbf,cbsf,p等参数和成员函数AddCallBack中各参数一样。第一个RemoveCallBack用于删除注册在事件event上某回调工具的一个成员函数。第二个RemoveCallBack用于删除注册在事件event上的某普通函数或某回调类的一个静态成员函数。第三个RemoveCallBack用于删除注册在事件event上的全部回调函数。
二、CallBack类的利用要领
利用CallBack类,可按以下步调举办:
1.确定措施中哪些工具间存在干系,需要成立动静毗连。并确定在各特定动静毗连干系中,哪个工具是事件工具,哪个工具是回调工具。
2.事件工具类和回调工具类都必需从CallBack类担任,以得到回调支持。
3.为事件工具注册回调数据。包罗:事件名,回调函数名,指向回调工具的指针。
4.当你感乐趣的事件产生时,在事件工具类激发事件的成员函数中挪用CallCallBack函数。
下面是一个详细的例子。通过它你会对Callback类的利用要领有进一步的相识。
//测试措施文件:test.cpp
#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;
}
};
void main(void)
{
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
}
运行功效:
音量改变了.
喂!太吵了!此刻音量是:22
音量改变了.
啊!我听不清了。此刻音量是:2
在上例中,扬声器工具s为事件工具,耳朵工具e为回调工具。。s上被注册了三个事件:“音量改变了”,“音量大于20”,“音量小于5”。 回调函数别离为:Ear::Response, Ear::HighVoiceResponse,Ear::LowVoiceResponse。当扬声器s通过其成员函数IncreaseVolume和 DecreaseVolume改变音量时,回调工具e会自行动出回响。可见,通过利用CallBack类,在工具间成立动静毗连已变为一项很简朴和美妙的事情。
由于笔者程度有限,该类的设计必有不完善之处。假如您对它感乐趣,笔者可与列位C++玩家配合探讨这类问题。接洽方法:[email protected]
附:措施清单(本措施在MS VC++5.0和TC++3.0上均编译通过) //回调类的类布局:callback.h
#ifndef _CALLBACK_H
#define _CALLBACK_H
#include<stdlib.h>
#include<string.h>
#include<iostream.h>
#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.cpp
#include"callback.h"
//EventRecord类的实现
EventRecord::EventRecord(void)
{
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)
{
//按初始尺寸为回调表分派内存空间
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;
//寻找因删除事件记录而发生的第一个空闲位置,并填写新事件记录
for(int 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;
for(int 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();
}
}