怎么理解回调函数? 回调函数合集
網上查了一通,有點體會,特來分享與討論。
?
****************************************************************************************************************************
SECTION 1
****************************************************************************************************************************
回調函數
回調函數是一個時時聽到的概念,比如在windows API編程時遇到的WinProc函數,就是我們編寫而由操作系統調用的函數。現在,我們需要慢慢又詳細的記錄一下這個問題。
庫與使用者的問題
在開始之前,首先我們想像這樣一個情景,一個大型軟件公司開發一套軟件庫提供給用戶使用。在這句話中,出現兩個對立面,一個是軟件公司,一個是用戶。顯然,軟件公司實力很強大,他們強大的地方在于他們很聰明。請看下圖:
?
庫開發者是提供方,他們不知道用戶想做什么,庫開發者對用戶的信息是未知的,因為兩邊的人誰也沒見過誰,就像一個人在美國一個人在中國。他們聰明的地方就在于即使不知道用戶怎么操作,他們也可以用一種通用的方法來解決用戶的問題。換句話說,庫開發方不管用戶怎么折騰變化,他都能適應。這就是問題的神奇之處,當然,為了達到這種效果,必須有一種規則來約束。
規則?
現在來說規則,規則比較好理解,就是兩方達成的約束。比如甲和乙兩個人,甲對乙說,你只要提供給我一個塑料球,我都能給它涂上顏色。這里的約束是塑料球,(當然,為了簡化問題這里的約束還是比較泛泛而不嚴謹的),這個時候如果乙給他一個水晶球,或者給他一輛汽車都不行,因為已經超越了約束。所以說,庫開發者能適應用戶的各種變化就在于他也給了用戶一定的約束,是在約束之下的適應,這也是不言自明的。
代碼之下無秘密
下面一段代碼極好的解釋了上述問題:
#include <stdio.h>
typedef int student_id;
typedef int student_age;
typedef struct _Student{
??? student_id id;
??? student_age age;
}Student;
//類型重定義:函數指針類型
typedef bool (*pFun)(Student, Student);
//-----------------------------------------------
//冒泡排序法:能夠按AGE或ID排序,用同一個函數實現
//-----------------------------------------------
void sort(Student stu[],const int num,pFun fun)
{
??? Student temp;
???
??? for(int i = 0; i < num; ++i)
{
??????? for(int j = 0; j < num - i -1; ++j)
??????? {
??????????? if((*fun)(stu[j],stu[j+1]))
??????????? {
??????????????? temp = stu[j];
??????????????? stu[j] = stu[j+1];
??????????????? stu[j+1] = temp;
??????????? }
??????? }
??? }
}
//-----------------------------------------------
//回調函數:比較年齡
//-----------------------------------------------
bool CompareAge(Student stu1,Student stu2)
{
??? //更改從大到小還是從小到大的順序,只需反一下。
??? if(stu1.age < stu2.age)
??????? return true;
??? return false;
}
//-----------------------------------------------
//回調函數:比較id
//-----------------------------------------------
bool CompareId(Student stu1,Student stu2)
{
??? //更改從大到小還是從小到大的順序,只需反一下。
??? if(stu1.id < stu2.id)
??????? return true;
??? return false;
}
int main()
{
??? Student stu[] = {
??? {1103,24},
??? {1102,23},
??? {1104,22},
??? {1107,25},
??? {1105,21}};
???
??? pFun fun = CompareAge;
??? int size = sizeof(stu)/sizeof(Student);
???
??? sort(stu,size,fun);
???
??? for(int i = 0; i < size; ++i){
??????? printf("%d %d\n",stu[i].id,stu[i].age);
??? }
???
??? return 0;
}
?
通過代碼,我們必須分辨出哪個是庫提供方,哪個是用戶:
?
sort(...)方法是庫開者提供的,只是因為模擬問題所以把它寫在一個文件中了,我們可以假想它存放在某個靜態庫lib里面,這樣就和它有了界限。
庫開發者提供方法sort(...)供我們調用,形參里面還包括一個函數指針,就是調用用戶自己寫的函數,也即回調函數。所以它提供了規則,也就是說這個回調函數的形參是什么,返回值又是什么,假如用戶對于這個回調函數的形參和返回值啥都不知道,隨意寫了一個函數放進去,sort方法能接受嗎?顯然是不行的。
那么用戶怎么知道回調函數的形參和返回值等信息呢?庫開發方會告知,或者在它的文檔里面會有說明,就像在windows API開發窗口程序時提供的WinProc() 函數一樣,里面的形參必須一模一樣。(這個地方會牽涉到接口)
什么是回?
根據上面的代碼,我們知道,庫開發方提供了一個方法,然后形參又帶有一個函數指針,等著用戶去寫然后拿來調用,并在其實現中調用了這個函數。站在庫開發方的立場,我們可以總結為一句話:庫開發方調用了用戶的函數(回調函數)。
現在,我們把視角移到回調函數的立場上來看,它被調用了。但同時它也有參數,且傳進來的參數是庫開發方提供的數據,這里我們又可以總結一句話:回調函數調用了庫開發方的數據。
這就是“回”的意思,你調我我也調用你,雙方互相調用。
?
回調函數的重點是什么?
根據上文分析,發現回調函數其實也不是難于理解,我覺得重點在于一定要劃分清楚誰是庫開發方(也可稱為提供方,調用者),誰是用戶,把職能劃分清楚是最關鍵的。
?
****************************************************************************************************************************
SECTION 2
****************************************************************************************************************************
?
tip1
你就想象你函數的一部分功能被外包給別人。至于被人怎么實現的你不用管,你的函數具有一個完整的功能,但是有的功能可以隨你自己定制,參照stl中的for_each
tip2
簡單點說,用戶是實現方,實現方需要調用A()函數,但為了A()函數具有通用性,需要根據實現方的意愿調用實現方提供的函數cbB(),在這里cbB()即為回調函數。在Windows編程中回調函數用途很廣泛。
?
****************************************************************************************************************************
SECTION 3
****************************************************************************************************************************
回調函數是通過函數指針調用的函數:把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,就稱為回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
?
通俗點說就是:在A類中調用B類中的某個方法C,然后B類反過來調用A類中的方法D,則D就是回調函數。
?
打個比方:
我們將A類看成一個人,就叫他小A;將B類也看成一個人,就叫他小B;
那么使用回調函數D的過程就可以這樣理解:
小A在開發過程中遇到了一個麻煩,而這個麻煩只有小B才能解決,于是小A找到了小B求他幫忙,但是由于和小B不怎么熟悉于是帶上了名片。小A向小B說明了遇到的麻煩后,正巧小B正在忙于其他事情,于是小B先收下了小A的名片,告訴小A回去等消息。由于這個麻煩不解決就無法繼續開發,于是回去等消息的小A就只好先去做別的事情。過了一段時間小B忙完手上的事情,解決了小A的麻煩后,找出小A名片上的電話號碼撥了過去,告訴小A,麻煩已經搞定了(小B只是順著名片把解決方案告訴給小A,而并不關心自己給出的解決方案在小A那里會如何運用)。小A放下電話后,利用小B給他的解決方案繼續開發。
簡而言之:小A帶著名片D通過途徑C找到小B求他幫忙,小B不能立即解決于是收下名片D,之后的某天小B解決了小A的問題后又通過名片D告訴了小A解決方法。
即:
A類調用B類中的C方法,D作為函數指針當做C方法的一個參數(小A帶著名片D通過途徑C找到小B求他幫忙)
B類無法立即處理,就先進行回調函數標記(收下名片)
在未來的某一個時間點,當滿足觸發條件時(解決問題后)
通過回調函數D傳遞回信息給A類(通過名片告訴結果)
?
?
?下面以一個例子說明上述過程(解釋見注釋):
?
總結
以上是生活随笔為你收集整理的怎么理解回调函数? 回调函数合集的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 产品经理必懂技术术语(前端类)
- 下一篇: 数据表格应该这样设计