欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
我的博客

關(guān)于回調函數


一,回調函數

我們經(jīng)常在C++設計時(shí)通過(guò)使用回調函數可以使有些應用(如定時(shí)器事件回調處理、用回調函數記錄某操作進(jìn)度等)變得非常方便和符合邏輯,那么它的內在機制如何呢,怎么定義呢?它和其它函數(比如鉤子函數)有何不同呢?

使用回調函數實(shí)際上就是在調用某個(gè)函數(通常是API函數)時(shí),將自己的一個(gè)函數(這個(gè)函數為回調函數)的地址作為參數傳遞給那個(gè)函數。

而那個(gè)函數在需要的時(shí)候,利用傳遞的地址調用回調函數,這時(shí)你可以利用這個(gè)機會(huì )在回調函數中處理消息或完成一定的操作。至于如何定義回調函數,跟具體使用的API函數有關(guān),一般在幫助中有說(shuō)明回調函數的參數和返回值等。C++中一般要求在回調函數前加CALLBACK(相當于FAR PASCAL),這主要是說(shuō)明該函數的調用方式。

至于鉤子函數,只是回調函數的一個(gè)特例。習慣上把與SetWindowsHookEx函數一起使用的回調函數稱(chēng)為鉤子函數。也有人把利用VirtualQueryEx安裝的函數稱(chēng)為鉤子函數,不過(guò)這種叫法不太流行。

也可以這樣,更容易理解:回調函數就好像是一個(gè)中斷處理函數,系統在符合你設定的條件時(shí)自動(dòng)調用。為此,你需要做三件事:

1.聲明;

2.定義;

3.設置觸發(fā)條件,就是在你的函數中把你的回調函數名稱(chēng)轉化為地址作為一個(gè)參數,以便于系統調用。

聲明和定義時(shí)應注意:回調函數由系統調用,所以可以認為它屬于WINDOWS系統,不要把它當作你的某個(gè)類(lèi)的成員函數。


二,回調函數、消息和事件例程


調用(calling)機制從匯編時(shí)代起已經(jīng)大量使用:準備一段現成的代碼,調用者可以隨時(shí)跳轉至此段代碼的起始地址,執行完后再返回跳轉時(shí)的后續地址。CPU為此準備了現成的調用指令,調用時(shí)可以壓棧保護現場(chǎng),調用結束后從堆棧中彈出現場(chǎng)地址,以便自動(dòng)返回。借堆棧保護現場(chǎng)真是一項絕妙的發(fā)明,它使調用者和被調者可以互不相識,于是才有了后來(lái)的函數和構件。

此調用機制并非完美?;卣{函數就是一例。函數之類(lèi)本是為調用者準備的美餐,其烹制者應對食客了如指掌,但實(shí)情并非如此。例如,寫(xiě)一個(gè)快速排序函數供他人調用,其中必包含比較大小。麻煩來(lái)了:此時(shí)并不知要比較的是何類(lèi)數據--整數、浮點(diǎn)數、字符串?于是只好為每類(lèi)數據制作一個(gè)不同的排序函數。更通行的辦法是在函數參數中列一個(gè)回調函數地址,并通知調用者:君需自己準備一個(gè)比較函數,其中包含兩個(gè)指針類(lèi)參數,函數要比較此二指針所指數據之大小,并由函數返回值說(shuō)明比較結果。排序函數借此調用者提供的函數來(lái)比較大小,借指針傳遞參數,可以全然不管所比較的數據類(lèi)型。被調用者回頭調用調用者的函數(夠咬嘴的),故稱(chēng)其為回調(callback)。

回調函數使程序結構亂了許多。Windows API 函數集中有不少回調函數,盡管有詳盡說(shuō)明,仍使初學(xué)者一頭霧水??峙逻@也是無(wú)奈之舉。

無(wú)論何種事物,能以樹(shù)形結構單向描述畢竟讓人舒服些。如果某家族中孫輩又是某祖輩的祖輩,恐怕無(wú)人能理清其中的頭緒。但數據處理之復雜往往需要構成網(wǎng)狀結構,非簡(jiǎn)單的客戶(hù)/服務(wù)器關(guān)系能窮盡。

Windows 系統還包含著(zhù)另一種更為廣泛的回調機制,即消息機制。消息本是 Windows 的基本控制手段,乍看與函數調用無(wú)關(guān),其實(shí)是一種變相的函數調用。發(fā)送消息的目的是通知收方運行一段預先準備好的代碼,相當于調用一個(gè)函數。消息所附帶的 WParam 和 LParam 相當于函數的參數,只不過(guò)比普通參數更通用一些。應用程序可以主動(dòng)發(fā)送消息,更多情況下是坐等 Windows 發(fā)送消息。一旦消息進(jìn)入所屬消息隊列,便檢感興趣的那些,跳轉去執行相應的消息處理代碼。操作系統本是為應用程序服務(wù),由應用程序來(lái)調用。而應用程序一旦啟動(dòng),卻要反過(guò)來(lái)等待操作系統的調用。這分明也是一種回調,或者說(shuō)是一種廣義回調。其實(shí),應用程序之間也可以形成這種回調。假如進(jìn)程 B 收到進(jìn)程 A 發(fā)來(lái)的消息,啟動(dòng)了一段代碼,其中又向進(jìn)程 A 發(fā)送消息,這就形成了回調。這種回調比較隱蔽,弄不好會(huì )搞成遞歸調用,若缺少終止條件,將會(huì )循環(huán)不已,直至把程序搞垮。若是故意編寫(xiě)成此遞歸調用,并設好終止條件,倒是很有意思。但這種程序結構太隱蔽,除非十分必要,還是不用為好。

利用消息也可以構成狹義回調。上面所舉排序函數一例,可以把回調函數地址換成窗口 handle。如此,當需要比較數據大小時(shí),不是去調用回調函數,而是借 API 函數 SendMessage 向指定窗口發(fā)送消息。收到消息方負責比較數據大小,把比較結果通過(guò)消息本身的返回值傳給消息發(fā)送方。所實(shí)現的功能與回調函數并無(wú)不同。當然,此例中改為消息純屬畫(huà)蛇添腳,反倒把程序搞得很慢。但其他情況下并非總是如此,特別是需要異步調用時(shí),發(fā)送消息是一種不錯的選擇。假如回調函數中包含文件處理之類(lèi)的低速處理,調用方等不得,需要把同步調用改為異步調用,去啟動(dòng)一個(gè)單獨的線(xiàn)程,然后馬上執行后續代碼,其余的事讓線(xiàn)程慢慢去做。一個(gè)替代辦法是借 API 函數 PostMessage 發(fā)送一個(gè)異步消息,然后立即執行后續代碼。這要比自己搞個(gè)線(xiàn)程省事許多,而且更安全。

如今我們是活在一個(gè) object 時(shí)代。只要與編程有關(guān),無(wú)論何事都離不開(kāi) object。但 object 并未消除回調,反而把它發(fā)揚光大,弄得到處都是,只不過(guò)大都以事件(event)的身份出現,鑲嵌在某個(gè)結構之中,顯得更正統,更容易被人接受。應用程序要使用某個(gè)構件,總要先弄清構件的屬性、方法和事件,然后給構件屬性賦值,在適當的時(shí)候調用適當的構件方法,還要給事件編寫(xiě)處理例程,以備構件代碼來(lái)調用。何謂事件?它不過(guò)是一個(gè)指向事件例程的地址,與回調函數地址沒(méi)什么區別。

不過(guò),此種回調方式比傳統回調函數要高明許多。首先,它把讓人不太舒服的回調函數變成一種自然而然的處理例程,使編程者頓覺(jué)氣順。再者,地址是一個(gè)危險的東西,用好了可使程序加速,用不好處處是陷阱,程序隨時(shí)都會(huì )崩潰?,F代編程方式總是想法把地址隱藏起來(lái)(隱藏比較徹底的如 VB 和 Java),其代價(jià)是降低了程序效率。事件例程(?)使編程者無(wú)需直接操作地址,但并不會(huì )使程序減速。
(例程似乎是進(jìn)程的臺灣翻譯。)


三,精妙比喻:回調函數還真有點(diǎn)像您隨身帶的BP機:告訴別人號碼,在它有事情時(shí)Call您。

回調用于層間協(xié)作,上層將本層函數安裝在下層,這個(gè)函數就是回調,而下層在一定條件下觸發(fā)回調,例如作為一個(gè)驅動(dòng),是一個(gè)底層,他在收到一個(gè)數據時(shí),除了完成本層的處理工作外,還將進(jìn)行回調,將這個(gè)數據交給上層應用層來(lái)做進(jìn)一步處理,這在分層的數據通信中很普遍。其實(shí)回調和API非常接近,他們的共性都是跨層調用的函數。但區別是API是低層提供給高層的調用,一般這個(gè)函數對高層都是已知的;而回調正好相反,他是高層提供給底層的調用,對于低層他是未知的,必須由高層進(jìn)行安裝,這個(gè)安裝函數其實(shí)就是一個(gè)低層提供的API,安裝后低層不知道這個(gè)回調的名字,但它通過(guò)一個(gè)函數指針來(lái)保存這個(gè)回調,在需要調用時(shí),只需引用這個(gè)函數指針和相關(guān)的參數指針。??? 其實(shí):回調就是該函數寫(xiě)在高層,低層通過(guò)一個(gè)函數指針保存這個(gè)函數,在某個(gè)事件的觸發(fā)下,低層通過(guò)該函數指針調用高層那個(gè)函數。



軟件模塊之間總是存在著(zhù)一定的接口,從調用方式上,可以把他們分為三類(lèi):同步調用、回調和異步調用。同步調用是一種阻塞式調用,調用方要等待對方執行完畢才返回,它是一種單向調用;回調是一種雙向調用模式,也就是說(shuō),被調用方在接口被調用時(shí)也會(huì )調用對方的接口;異步調用是一種類(lèi)似消息或事件的機制,不過(guò)它的調用方向剛好相反,接口的服務(wù)在收到某種訊息或發(fā)生某種事件時(shí),會(huì )主動(dòng)通知客戶(hù)方(即調用客戶(hù)方的接口)?;卣{和異步調用的關(guān)系非常緊密,通常我們使用回調來(lái)實(shí)現異步消息的注冊,通過(guò)異步調用來(lái)實(shí)現消息的通知。同步調用是三者當中最簡(jiǎn)單的,而回調又常常是異步調用的基礎。
 
對于不同類(lèi)型的語(yǔ)言(如結構化語(yǔ)言和對象語(yǔ)言)、平臺(Win32、JDK)或構架(CORBA、DCOM、WebService),客戶(hù)和服務(wù)的交互除了同步方式以外,都需要具備一定的異步通知機制,讓服務(wù)方(或接口提供方)在某些情況下能夠主動(dòng)通知客戶(hù),而回調是實(shí)現異步的一個(gè)最簡(jiǎn)捷的途徑。

對于一般的結構化語(yǔ)言,可以通過(guò)回調函數來(lái)實(shí)現回調?;卣{函數也是一個(gè)函數或過(guò)程,不過(guò)它是一個(gè)由調用方自己實(shí)現,供被調用方使用的特殊函數。

在面向對象的語(yǔ)言中,回調則是通過(guò)接口或抽象類(lèi)來(lái)實(shí)現的,我們把實(shí)現這種接口的類(lèi)成為回調類(lèi),回調類(lèi)的對象成為回調對象。對于象C++或Object Pascal這些兼容了過(guò)程特性的對象語(yǔ)言,不僅提供了回調對象、回調方法等特性,也能兼容過(guò)程語(yǔ)言的回調函數機制。

Windows平臺的消息機制也可以看作是回調的一種應用,我們通過(guò)系統提供的接口注冊消息處理函數(即回調函數),從而實(shí)現接收、處理消息的目的。由于Windows平臺的API是用C語(yǔ)言來(lái)構建的,我們可以認為它也是回調函數的一個(gè)特例。

對于分布式組件代理體系CORBA,異步處理有多種方式,如回調、事件服務(wù)、通知服務(wù)等。事件服務(wù)和通知服務(wù)是CORBA用來(lái)處理異步消息的標準服務(wù),他們主要負責消息的處理、派發(fā)、維護等工作。對一些簡(jiǎn)單的異步處理過(guò)程,我們可以通過(guò)回調機制來(lái)實(shí)現。

下面我們集中比較具有代表性的語(yǔ)言(C、Object Pascal)和架構(CORBA)來(lái)分析回調的實(shí)現方式、具體作用等。

2 過(guò)程語(yǔ)言中的回調(C)


2.1 函數指針
回調在C語(yǔ)言中是通過(guò)函數指針來(lái)實(shí)現的,通過(guò)將回調函數的地址傳給被調函數從而實(shí)現回調。因此,要實(shí)現回調,必須首先定義函數指針,請看下面的例子:

void Func(char *s);// 函數原型
void (*pFunc) (char *);//函數指針

可以看出,函數的定義和函數指針的定義非常類(lèi)似。

一般的化,為了簡(jiǎn)化函數指針類(lèi)型的變量定義,提高程序的可讀性,我們需要把函數指針類(lèi)型自定義一下。
typedef void(*pcb)(char *);

回調函數可以象普通函數一樣被程序調用,但是只有它被當作參數傳遞給被調函數時(shí)才能稱(chēng)作回調函數。

被調函數的例子:

void GetCallBack(pcb callback)
{
 /*do something*/
}
用戶(hù)在調用上面的函數時(shí),需要自己實(shí)現一個(gè)pcb類(lèi)型的回調函數:
void fCallback(char *s)
{
/* do something */
}
然后,就可以直接把fCallback當作一個(gè)變量傳遞給GetCallBack,
GetCallBack(fCallback);

如果賦了不同的值給該參數,那么調用者將調用不同地址的函數。賦值可以發(fā)生在運行時(shí),這樣使你能實(shí)現動(dòng)態(tài)綁定。

2.2 參數傳遞規則
到目前為止,我們只討論了函數指針及回調而沒(méi)有去注意ANSI C/C++的編譯器規范。許多編譯器有幾種調用規范。如在Visual C++中,可以在函數類(lèi)型前加_cdecl,_stdcall或者_pascal來(lái)表示其調用規范(默認為_(kāi)cdecl)。C++ Builder也支持_fastcall調用規范。調用規范影響編譯器產(chǎn)生的給定函數名,參數傳遞的順序(從右到左或從左到右),堆棧清理責任(調用者或者被調用者)以及參數傳遞機制(堆棧,CPU寄存器等)。

將調用規范看成是函數類(lèi)型的一部分是很重要的;不能用不兼容的調用規范將地址賦值給函數指針。例如:

// 被調用函數是以int為參數,以int為返回值
 __stdcall int callee(int);

// 調用函數以函數指針為參數
 void caller( __cdecl int(*ptr)(int));

// 在p中企圖存儲被調用函數地址的非法操作
__cdecl int(*p)(int) = callee; // 出錯

 指針p和callee()的類(lèi)型不兼容,因為它們有不同的調用規范。因此不能將被調用者的地址賦值給指針p,盡管兩者有相同的返回值和參數列

2.3 應用舉例
C語(yǔ)言的標準庫函數中很多地方就采用了回調函數來(lái)讓用戶(hù)定制處理過(guò)程。如常用的快速排序函數、二分搜索函數等。

快速排序函數原型:

void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
二分搜索函數原型:
void *bsearch(const void *key, const void *base, size_t nelem,
size_t width, int (_USERENTRY *fcmp)(const void *, const void *));

其中fcmp就是一個(gè)回調函數的變量。

下面給出一個(gè)具體的例子:

#i nclude
#i nclude

int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };

int main(void)
{
int x;

qsort((void *)list, 5, sizeof(list[0]), sort_function);
for (x = 0; x < 5; x++)
printf("%in", list[x]);
return 0;
}

int sort_function( const void *a, const void *b)
{
return *(int*)a-*(int*)b;
}

2.4 面向對象語(yǔ)言中的回調(Delphi)

Dephi與C++一樣,為了保持與過(guò)程語(yǔ)言Pascal的兼容性,它在引入面向對象機制的同時(shí),保留了以前的結構化特性。因此,對回調的實(shí)現,也有兩種截然不同的模式,一種是結構化的函數回調模式,一種是面向對象的接口模式。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
C++筆記點(diǎn)滴
函數參數的傳遞
高質(zhì)量C/C++編程(片段)
const在函數前與函數后的區別
const的作用
C++中const用法總結
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久