實際工做中,對於回調函數一直是我不肯意去觸碰的東西,一來因爲被不少人搞得神祕兮兮的覺得是很高深難懂的技術,二來在通常狀況下經過互相包含類指針也可以解決問題,因此一直就不想去研究這個東東,最近一個項目中被經理逼着使用了回調函數,切切實實體會到了它的好處,代碼中類之間的關係不再那麼錯綜複雜了,類A想告訴類B一個事情不再須要在A中定義B的指針了,定義回調函數便可。下面把我理解的回調函數寫出來,拋出一塊磚,箇中滋味要各位看客多實踐才能體會出來。編程
一、基礎知識網絡
所謂回調,就是模塊A要經過模塊B的某個函數b()完成必定的功能,可是函數b()本身沒法實現所有功能,須要反過頭來調用模塊A中的某個函數a()來完成,這個a()就是回調函數。以下圖模塊化
①約定接口規範。在模塊B必須約定接口規範,也就是定義回調函數a()的函數原型函數
這裏回調函數原型的定義最好遵循typedef void (*SCT_XXX)(LPVOID lp, const CBParamStruct& cbNode); SCT_XXX是回調函數名稱,lp是回調上下文,CBParamStruct是回調參數,通常因爲要回調的參數不止一個,因此定義一個結構體比較方便。this
②回調函數的註冊。爲了讓模塊B知道本身將要使用的回調函數,必須有一個函數或語句來註冊回調函數spa
註冊回調函數的定義遵循void RCF_XXX(SCT_XXX pfn, LPVOID lp); RCF_XXX是註冊函數名,pfn是回調函數名稱(是指針),lp是回調上下文。通常在A模塊初始化完B模塊後調用,將A模塊中定義的回調函數地址賦值給pfn,lp賦值爲this。 線程
③在模塊A中要作的事情:指針
首先將回調函數聲明成靜態的,static void CF_XXX(LPVOID lp, const CBParamStruct& cbNode); 函數的參數必須與B模塊中回調函數原型的參數保持一致。對象
初始化B模塊時,調用註冊函數將模塊A中聲明的回調函數CF_XXX的地址傳給pfn,即pfn=CF_XXX;(函數名稱CF_XXX實際上是個指針,指向回調函數的地址) 。blog
二、舉例
回調函數使用第一個場景:MFC界面編程。有這樣一個需求,主界面左側是一個樹形列表,右側是一個繪圖區用來展現左側列表項的內容,雙擊繪圖區彈出框用來編輯。通常的作法是在繪圖區對話框初始化時將主對話框或者樹形列表的指針傳進來,在繪圖區對話框中處理雙擊事件,在事件出來函數中調用主對話框或樹形列表的指針完成更新操做。這樣主對話框類和繪圖區對話框類之間就出現了互相包含的關係,回調函數這個時候就能夠大顯身手了,主對話框僅須要包含繪圖區對話框的頭文件和聲明一個繪圖區對話框的對象便可。具體作法是:在繪圖區對話框中定義回調函數原型和註冊回調的函數,並處理鼠標雙擊事件,在事件函數中發出回調通知。主對話框中按原型定義回調函數,在回調函數中完成樹形列表的更新。
回調函數的第二個應用場景:網絡編程。 在網絡編程中,爲了體現模塊化,通常把通信和數據處理劃分開來,即通信模塊負責協議定義、數據收發,而數據處理模塊只負責對收發的數據進行解析和打包,假如通信模塊開啓了一個線程在持續地接收數據,這個時候問題來了,它經過什麼手段把數據交到數據處理模塊手中呢?每次收到數據,拿到數據處理模塊的指針完成相關操做,這樣有犯了兩個類指針互相指的錯誤,也破壞了兩個模塊的獨立性。使用回調函數這些問題都迎刃而解了,下面給出部分僞代碼: