C++中建立物件間訊息連線的一種系統方法 (轉)
C++中建立間訊息連線的一種方法
項飛
用過C++進行過物件導向設計的都知道,程式中的物件很少單獨存在。不考慮物件間的相互作用幾乎是不可能的。所以,標識物件間的關係或建立物件間的訊息連線是物件導向程式設計的一項重要任務。本文著重從C++程式設計的角度,提出一種建立物件間訊息連線的實用方法。如果你想詳細瞭解物件導向程式設計技術,請參閱有關專著。大家都知道物件是資料和方法的封裝體。在C++中,它們分別表現為資料成員和成員。程式設計者透過物件的各種方法,來改變物件的狀態(即改變物件的屬性資料)。從而使該物件發生某些“事件”。當一物件發生某事件時,它通常需向其它相關物件傳送“訊息”,請求它們作出一些處理。 這時,發生事件並向其它物件請求處理的物件被稱為“事件物件”,而處理事件的物件被稱為“回撥物件”。回撥物件對事件的處理稱為“回撥函式”。在C++中,這一過程相當於:當事件物件發生事件時,回撥物件的某些成員函式。通常的作法是回撥物件向事件物件傳遞物件指標。但這種方法不通用。為了減少程式設計的工作量,本文提出一種建立物件間訊息連線的系統方法。它的思路是:將“事件發生→請求處理→執行處理”這一過程抽象成一個“回撥”(CallBack)類。透過繼承,使用者可以輕鬆獲取建立物件間訊息連線的機制。
一、回撥類的資料結構及其成員函式
本文提出的CallBack類支援三種回撥函式。它們是:回撥物件中的成員函式,屬於回撥類的靜態成員函式和普通的C函式。CallBackle類中包含一回撥函式表callBackList。它用於記錄事件名稱,指向回撥函式及回撥物件的指標。該表的每一個節點為一個事件記錄EventRecord。每個事件記錄包含三個域:事件名指標eventName,指向回撥物件的指標pointerToCBO,指向回撥函式的指標pointerToCBF或pointerToCB(其中,pointerToCBF指向回撥物件的成員函式,pointerToCBSF指向回撥類的靜態成員函式或普通函式。它們同處於一共用體內)。CallBack類所提供的回撥機制是這樣的:在事件物件上註冊回撥物件中的回撥函式;當事件發生時,事件物件在其回撥錶中檢索並執行回撥函式。從而使二者的訊息連線得以建立。(關於該類的具體實現,請參閱文後所附的程式清單) 回撥物件
事件物件
事件名 回撥物件指標 回撥函式指標
“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分別是指向成員函式及靜態成員函式(或普通函式)的指標。當回撥函式來自某回撥物件Some時,傳遞成員函式指標應採用如下格式:(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);
其中,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<
}
void HighVoiceResponse(CallData callData)//對高音的反應
{
cout<
}
void LowVoiceResponse(CallData callData)// 對低音的反應
{
cout<
}
};
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++玩家共同探討這類問題。聯絡方to:式:fei_xiang@263">式:fei_xiang@263.net
附:程式清單(本程式在MS VC++5.0和TC++3.0上均編譯透過)
調類的類結構:callback.h
#ifndef _CALLBACK_H
#define _CALLBACK_H
#include
#include
#include
#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) { curp= 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(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(void)
{
初始尺寸為回撥錶分配空間
callBackList = new EventRecord[CALLBACKLIST_INIT_SIZE];
if(!callBackList){
cerr<
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<
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<
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 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<
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 if(callBackList[start].IsEmpty()){ callBackList[start] = EventRecord(event,pCBSF); break; } if(start < lastpos) return; hole is found if(lastpos == size) list is insufficient { EventRecord *tempList = callBackList; callBackList = new EventRecord[size + CALLBACKLIST_INCREMENT]; if(!callBackList){ cerr<
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(); } }
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-993055/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C++中建立物件的兩種方法及其區別C++物件
- C++中有三種建立物件的方法C++物件
- C++中建立持久物件的方法C++物件
- C++程式間通訊的十一種方法C++
- 最全--Java中建立物件的5種方法Java物件
- 某運營商系統MQ訊息中介軟體連線中斷故障MQ
- unix系統當中 軟連線與硬連線的區別(轉)
- JavaScript建立物件的多種方法JavaScript物件
- Cisco 交換機之間的連線方法(轉)
- Kafka中避免重複訊息的5種有效方法Kafka
- win10系統寬頻連線怎麼建立 win10電腦建立寬頻連線的方法Win10
- 一種實現資料庫連線池的方法(JAVA) (轉)資料庫Java
- 解析帶emoji和連結的聊天系統訊息
- JS中建立物件的方法JS物件
- 時間管理與其他系統的連線
- IM開發乾貨分享:淺談IM系統中離線訊息、歷史訊息的最佳實踐
- 使用 Excel cdata addin 連線 SAP ABAP 系統時遇到錯誤訊息Excel
- 資料庫連線的方法種種資料庫
- Java中建立物件的5種方式Java物件
- js中建立物件的幾種方式JS物件
- Win10系統中本地連線空白的解決方法Win10
- LINUX系統中動態連結庫的建立與使用(轉)Linux
- 程式間的通訊實現(IPC)的11種方法 (轉)
- 一個人就需要物件之js中八種建立物件方式物件JS
- netty建立數萬客戶端連線,並主動發訊息Netty客戶端
- [轉載]javascript建立物件的幾種方式JavaScript物件
- 深度解析VC中的訊息(中) (轉)
- UNIX系統中Shell的一種新應用(轉)
- SAP ECC系統連線SAP PI系統的系統連線配置
- oracle 各種表間連線Oracle
- C++ BUILDER 訊息處理的深入探索 (轉)C++UI
- RocksDB 在 vivo 訊息推送系統中的實踐
- 建立一個連線資料庫的VB元件 (轉)資料庫元件
- js中建立物件的幾種常用方式JS物件
- JavaScript建立物件4種方法詳解JavaScript物件
- Unreal Cook Book:建立物件的的幾種姿勢(C++)Unreal物件C++
- ss:檢視網路連線的另一種方法
- win10系統本地連線在哪 win10系統開啟本地連線的方法Win10