對於回呼函數的編寫始終是寫特殊處理功能程式時用到的技巧之一。先介紹一下回調的使用基本方法與原理。
1、在這裡設:回呼函數為A()(這是最簡單的情況,不帶參數,但我們應用的實際情況常常很會複雜),使用回呼函數的操作函數為B(), 但B函數是需要參數的,這個參數就是指向函數A的地址變數,這個變數一般就是函數指標。使用方法為:
int A(char *p); // 回呼函數 typedef int(*CallBack)(char *p) ; // 聲明CallBack 類型的函數指標 CallBack myCallBack ; // 聲明函數指標變數 myCallBack = A; // 得到了函數A的地址 |
B函數一般會寫為 B(CallBack lpCall,char * P,........); // 此處省略了p後的參數形式 。
所以回調機制可解為,函數B要完成一定功能,但他自己是無法實現全部功能的。 需要藉助於函數A來完成,也就是回呼函數。B的實現為:
B(CallBack lpCall,char *pProvide) { ........... // B 的自己實現功能語句 lpCall(PpProvide); // 藉助回調完成的功能 ,也就是A函數來處理的。 ........... // B 的自己實現功能語句 } // -------------- 使用例子 ------------- char *p = "hello!"; CallBack myCallBack ; myCallBack = A ; B(A, p); |
以上就是回調的基本應用,本文所說的變身,其實是利用傳入不同的函數地址,實現調用者類與回呼函數所在類的不同轉換。
1、問題描述
CUploadFile 類完成資料上傳,與相應的介面進度顯示。
主要函數Send(...) 和回呼函數 GetCurState() ;
class CUploadFile : public CDialog { ...... int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) ; static int GetCurState(int nCurDone, int nInAll, void * pParam) ; ...... } int CUploadFile ::Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) { ... // 匯出傳輸資料的函數 int ret = Upload( (LPSTR)(LPCTSTR)m_strData, GetCurState, // 在這個回呼函數中處理介面 this, // CUploadFile 的自身指標 ,也就是pParam 所接受的參數 (LPSTR)(LPCTSTR)UploadFilePath, "", "", ); } int CUploadFile ::GetCurState(int nCurData, int nInAll, void * pParam) { ......... UploadFile *pThis = (UploadFile *)pParam; // nCurData 當前以傳出的資料量 // nInAll 總的資料量 // 有了pThis可以對介面進行各種操作了。 ............. } |
但大家仔細觀察就可以發現,這個類把資料傳送和介面顯示聚和到了一起,不容易得到複用。而且在複用過程中需要改動較多的地方 。
請大家記住現在的回呼函數傳入的類本身的靜態成員函數。
現在我們把資料的傳送和介面的顯示分離。回調則要傳入的是介面處理類的靜態函數。
介面處理類 CShowGUI,資料上傳類 CUploadData
class CUploadData { ...... typedef int(*SetUploadCaller)(int nCurData, int nInAll, void * pParam); int UploadFile(LPCTSTR lpFileNamePath,LPVOID lparam,SetUploadCaller Caller ); // 接受外界出入的參數,主要是回呼函數的地址通過參數Caller, int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) ; ...... // 注意此時不在需要GetCurState 函數了 。 }class CShowGUI: public CDialog { ....... typedef int(*SetUploadCaller)(int nCurData, int nInAll, void * pParam); void SetCallBack(LPCTSTR strPath); static int GetCurState(int nCurData, int nInAll, void * pParam) ; CUploadData m_Uploa d ; // 資料上傳類是介面顯示類的一個成員變數。 ....... } void CShowGUI :: SetCallBack(LPCTSTR strPath) { CUploadData myUploadData ; SetUploadCaller myCaller; // 聲明一個函數指標變數 myCaller = CurState ; // 取得介面處理函數的地址 myUploadData .UploadFile(strPath,this,myCaller); // 介面處理類的函數傳入,實現了資料傳入與介面處理的分離 . } |
通過上面的示範做到了介面與資料的分離,回呼函數分別扮演了不同角色,所以隨著處理問題的不同應靈活應用,但同樣因為處理資料類不知道介面處理類或外部調用類的類型,而更無法靈活地處理介面的不同顯示方式。這方面還希望喜歡鑽研技術的朋友繼續研究。