CALLBACK函數) – C/C++

來源:互聯網
上載者:User

調用(calling)機制從彙編時代起已經大量使用:準備一段現成的代碼,調用者可以隨時跳轉至此段代碼的起始地址,執行完後再返回跳轉時的後續地址。 CPU為此準備了現成的調用指令,調用時可以壓棧保護現場,調用結束後從堆棧中彈出現場地址,以便自動返回。借堆棧保護現場真是一項絕妙的發明,它使調用者和被調者可以互不相識,於是才有了後來的函數和構件,使吾輩編程者如此輕鬆愉快。若評選對人類影響最大之發明,在火與車輪之後,筆者當推壓棧調用。
   話雖這樣說,此調用機制並非完美。回呼函數就是一例。函數之類本是為調用者準備的美餐,其烹制者應對食客了如指掌,但實情並非如此。例如,寫一個快速排序函數供他人調用,其中必包含比較大小。麻煩來了:此時並不知要比較的是何類資料--整數、浮點數、字串?於是只好為每類資料製作一個不同的排序函數。更通行的辦法是在函數參數中列一個回呼函數地址,並通知調用者:君需自己準備一個比較函數,其中包含兩個指標類參數,函數要比較此二指標所指資料之大小,並由函數傳回值說明比較結果。排序函數藉此調用者提供的函數來比較大小,借指標傳遞參數,可以全然不管所比較的資料類型。被調用者回頭調用調用者的函數(夠咬嘴的),故稱其為回調(callback)。(STL中的Sort函數)
   回呼函數使程式結構亂了許多。Windows API 函數集中有不少回呼函數,儘管有詳盡說明,仍使初學者一頭霧水。恐怕這也是無奈之舉。無論何種事物,能以樹形結構單向描述畢竟讓人舒服些。如果某家族中孫輩又是某祖輩的祖輩,恐怕無人能理清其中的頭緒。但資料處理之複雜往往需要構成網狀結構,非簡單的客戶/伺服器關係能窮盡。
     Windows 系統還包含著另一種更為廣泛的回調機制,即訊息機制。訊息本是 Windows 的基本控制手段,乍看與函數調用無關,其實是一種變相的函數調用。發送訊息的目的是通知收方運行一段預先準備好的代碼,相當於調用一個函數。訊息所附帶的 WParam 和 LParam 相當於函數的參數,只不過比普通參數更通用一些。應用程式可以主動發送訊息,更多情況下是坐等 Windows 發送訊息。一旦訊息進入所屬訊息佇列,便檢感興趣的那些,跳轉去執行相應的訊息處理代碼。作業系統本是為應用程式服務,由應用程式來調用。而應用程式一旦啟動,卻要反過來等待作業系統的調用。這分明也是一種回調,或者說是一種廣義回調。其實,應用程式之間也可以形成這種回調。假如進程 B 收到進程 A 發來的訊息,啟動了一段代碼,其中又向進程 A 發送訊息,這就形成了回調。這種回調比較隱蔽,弄不好會搞成遞迴調用,若缺少終止條件,將會迴圈不已,直至把程式搞垮。若是故意編寫成此遞迴調用,並設好終止條件,倒是很有意思。但這種程式結構太隱蔽,除非十分必要,還是不用為好。
    我想起了寫的TTS的時候的兩個回調
    利用訊息也可以構成狹義回調。上面所舉排序函數一例,可以把回呼函數地址換成視窗handle。如此,當需要比較資料大小時,不是去調用回呼函數,而是借 API 函數 SendMessage 向指定視窗發送訊息。收到訊息方負責比較資料大小,把比較結果通過訊息本身的傳回值傳給訊息發送方。所實現的功能與回呼函數並無不同。當然,此例中改為訊息純屬畫蛇添腳,反倒把程式搞得很慢。但其他情況下並非總是如此,特別是需要非同步呼叫時,發送訊息是一種不錯的選擇。假如回呼函數中包含檔案處理之類的低速處理,調用方等不得,需要把同步調用改為非同步呼叫,去啟動一個單獨的線程,然後馬上執行後續代碼,其餘的事讓線程慢慢去做。一個替代辦法是借 API 函數PostMessage發送一個非同步訊息,然後立即執行後續代碼。這要比自己搞個線程省事許多,而且更安全。
    如今我們是活在一個 object 時代。只要與編程有關,無論何事都離不開 object。但 object 並未消除回調,反而把它發揚光大,弄得到處都是,只不過大都以事件(event)的身份出現,鑲嵌在某個結構之中,顯得更正統,更容易被人接受。應用程式要使用某個構件,總要先弄清構件的屬性、方法和事件,然後給構件屬性賦值,在適當的時候調用適當的構件方法,還要給事件編寫處理常式,以備構件代碼來調用。何謂事件?它不過是一個指向事件常式的地址,與回呼函數地址沒什麼區別。

   不過,此種回調方式比傳統回呼函數要高明許多。首先,它把讓人不太舒服的回呼函數變成一種自然而然的處理常式,使編程者頓覺氣順。再者,地址是一個危險的東西,用好了可使程式加速,用不好處處是陷阱,程式隨時都會崩潰。現代編程方式總是想法把地址隱藏起來(隱藏比較徹底的如 VB 和 Java),其代價是降低了程式效率。事件常式使編程者無需直接操作地址,但並不會使程式減速。更妙的是,此一改變,本是有損程式結構之奇技怪巧變成一種嶄新設計理念,不僅免去被人抨擊,而且逼得吾等凡人淨手更衣,細細研讀,仰慕至今。只是偶然靜心思慮,發覺不過一瓶舊酒而已,故引得此番議論,讓諸君見笑了。 事件驅動程式設計是圍繞著訊息基礎形成的,發生一個事件,伴隨著一大堆的訊息。

       我理解“回調機制”是window 在執行某個API函數的過程中,調用指定的一個函數。我們可以類比一下:
       假設 ms 提供一個函數叫做  EnumFont ,該函數是得到所有的字型,假設它的實現是
EnumFont()
{
  while ( (f =FindNextFont()) !=NULL)
  {
       printf("fontname: " + f.name);
  }
}
       這樣就迴圈顯示出所有的字型名稱。但是,開發人員可能對字型資訊另有用處,那麼如何才能讓開發人員能使用這些資訊呢,於是做改進:
EnumFont( void*  userFunc )
{
  while ( (f =FindNextFont()) !=NULL)
  {
       printf("fontname: " + f.name);
       if ( userFunc!=NULL)  userFunc( f) ;
  }
}
       假設userFunc 是一個函數 void f( FontObject font).這樣使用者只需要定義一個函數:
  void myfunc( FontObject font)
 {
                 listCtrl.Addstring ( font.name);
  }

       通過使用 EnumFont ( myfunc) 就可以將所有額字型資訊添加到一個列表框中。那麼我們稱 myfunc是一個回呼函數,即讓某個系統函數調用的函數。因此可以得出結論:
       1 回呼函數是由開發人員按照一定的原型進行定義的函數
       2 回呼函數並不由開發人員直接調用執行
       3 回呼函數通常作為參數傳遞給系統API,由該API來調用。
       4 回呼函數可能被系統API調用一次,也可能被迴圈調用多次。

       比如 函數int EnumFontFamilies(
                    HDC hdc,                    // handle to device control
                    LPCTSTR lpszFamily,  // pointer to family-name string
                    FONTENUMPROC lpEnumFontFamProc,  // pointer to callback function
                    LPARAM lParam        // pointer to application-supplied data
                   );
       其中的   FONTENUMPROC lpEnumFontFamProc就是一個回呼函數,該函數遵照格式int CALLBACK EnumFontFamProc( ENUMLOGFONT FAR *lpelf,  NEWTEXTMETRIC FAR *lpntm, int FontType,  LPARAM lParam )進行定義。

       回呼函數主要用於一些比較費時的操作,或響應不知道何時將會發生的事件,回呼函數提供了一種非同步機制,相對於同步執行,提高了效率.前者的例子如 WriteFileEx,ReadFileEx等,函數的最後一個參數是一個回呼函數的指標,程式中調用WriteFileEx以後,就直接返回了,可以繼續進行其他工作,系統在讀寫操作完成後通知程式作善後處理.後者的例子就是windows的事件機制回呼函數的另一個用途,是用於一些枚舉函數,如 EnumDisplayModes等,每找到一種支援的顯示模式,就通知回呼函數,由回呼函數具體處理,這是因為 EnumDisplayModes本身並不知道使用者要如何處理.能,使用者提供回呼函數,定製系統的功能,這樣,不同的使用者提供不同的回呼函數,可以使系統具有不同的功能.這就是所謂的plugin.使用回呼函數實際上就是在調用某個函數(通常是API函數)時,將自己的一個函數(這個函數為回呼函數)的地址作為參數傳遞給那個函數。而那個函數在需要的時候,利用傳遞的地址調用回呼函數,這時你可以利用這個機會在回呼函數中處理訊息或完成一定的操作。至於如何定義回呼函數,跟具體使用的API函數有關,一般在協助中有說明回呼函數的參數和傳回值等。
=========================================================================

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.