其實,回調函數大多隻是自己定義一個名字而已,函數體大多是系統定義好的,它有一個結構,一般一個代回調函數的的函數都有一個參數是接你的回調名的,它把一些值傳進回調函數(函數體包括參數是它預定好的,不能自己寫,除非全部函數都是你寫的),然後回調函數接受值,相應操作後将值傳回到原函數體(它的父親函數),最終讓原函數傳回一個值
我們經常在C++設計時通過使用回調函數可以使有些應用(如定時器事件回調處理、用回調函數記錄某操作進度等)變得非常友善和符合邏輯,那麼它的内在機制如何呢,怎麼定義呢?它和其它函數(比如鈎子函數)有何不同呢?這裡結合自己的使用經曆做一個簡單的介紹。
使用回調函數實際上就是在調用某個函數(通常是API函數)時,将自己的一個函數(這個函數為回調函數)的位址作為參數傳遞給那個函數。而那個函數在需要的時候,利用傳遞的位址調用回調函數,這時你可以利用這個機會在回調函數中處理消息或完成一定的操作。至于如何定義回調函數,跟具體使用的API函數有關,一般在幫助中有說明回調函數的參數和傳回值等。C++中一般要求在回調函數前加CALLBACK(相當于FAR PASCAL),這主要是說明該函數的調用方式。
至于鈎子函數,隻是回調函數的一個特例。習慣上把與SetWindowsHookEx函數一起使用的回調函數稱為鈎子函數。也有人把利用VirtualQueryEx安裝的函數稱為鈎子函數,不過這種叫法不太流行。
也可以這樣,更容易了解:回調函數就好像是一個中斷處理函數,系統在符合你設定的條件時自動調用。為此,你需要做三件事:
1.聲明;
2.定義;
3.設定觸發條件,就是在你的函數中把你的回調函數名稱轉化為位址作為一個參數,以便于系統調用。
聲明和定義時應注意:回調函數由系統調用,是以可以認為它屬于WINDOWS系統,不要把它當作你的某個類的成員函數
回調函數是一個程式員不能顯式調用的函數;通過将回調函數的位址傳給調用者進而實作調用。回調函數使用是必要的,在我們想通過一個統一接口實作不同的内容,這時用回掉函數非常合适。比如,我們為幾個不同的裝置分别寫了不同的顯示函數:void TVshow(); void ComputerShow(); void NoteBookShow()...等等。這是我們想用一個統一的顯示函數,我們這時就可以用回掉函數了。void show(void (*ptr)()); 使用時根據所傳入的參數不同而調用不同的回調函數。
不同的程式設計語言可能有不同的文法,下面舉一個c語言中回調函數的例子,其中一個回調函數不帶參數,另一個回調函數帶參數。
例子1:
// Test.c
#include < stdlib.h >
#include < stdio.h >
int Test1()
... {
int i;
for (i=0; i<30; i++)
...{
printf("The %d th charactor is: %c ", i, (char)('a' + i%26));
}
return 0;
}
int Test2( int num)
... {
int i;
for (i=0; i<num; i++)
...{
printf("The %d th charactor is: %c ", i, (char)('a' + i%26));
}
return 0;
}
void Caller1( void ( * ptr)()) // 指向函數的指針作函數參數
... {
(*ptr)();
}
void Caller2( int n, int ( * ptr)()) // 指向函數的指針作函數參數,這裡第一個參數是為指向函數的指針服務的,
// 不能寫成void Caller2(int (*ptr)(int n)),這樣的定義文法錯誤。
... {
(*ptr)(n);
return;
}
int main()
... {
printf("************************ ");
Caller1(Test1); //相當于調用Test2();
printf("&&&&&&************************ ");
Caller2(30, Test2); //相當于調用Test2(30);
return 0;
}
以上通過将回調函數的位址傳給調用者進而實作調用,但是需要注意的是帶參回調函數的用法。要實作回調,必須首先定義函數指針。函數指針的定義這裡稍微提一下。比如:
int (*ptr)(); 這裡ptr是一個函數指針,其中(*ptr)的括号不能省略,因為括号的優先級高于星号,那樣就成了一個傳回類型為整型的函數聲明了。