2012-03-06 51 views
0

我想知道callback functions在哪裏實際使用?回調函數的應用

是否有一種方法來實現C/C++中的回調函數(除了使用函數指針)?

請解釋一下下面這行 -

A callback can be used as a simpler alternative to polymorphism and generic programming

感謝

+0

可能是http://stackoverflow.com/questions/142789/what-is-a-callback-in-c-and-how-are-they-實現的副本 – Jay 2012-03-06 13:28:10

+0

2例子:[C標準庫的qsort函數](http://pubs.opengroup.org/onlinepubs/009695399/functions/qsort.html)和[Asynchronous I/O](http://en.wikipedia.org/wiki/Asynchronous_I/O#Callback_functions) - 我不會詳細說明,因爲我有一種感覺這是一項任務。 – pmdj 2012-03-06 13:29:22

+0

@Jay我已經通過了它..我是要求申請不執行!就像Core Audio編程就是那裏提到的一樣! – Vikram 2012-03-06 13:43:31

回答

5

我想知道回調函數在哪裏實際使用?

兩種最常見的用途是允許用戶執行的算法的一部分(例如,比較器傳遞至C的qsort和C++的std::sort允許任意類型的分選),並接收外部事件的通知(例如,在許多異步用戶界面和通信庫中,如Boost.Asio)。

在C/C++中有沒有一種方法來實現回調函數(除了使用函數指針)?

沒有C/C++這樣的語言。

在C中,唯一合理的機制是函數指針。

在C++中有很多方法。對於那些喜歡OOP的人來說,一種常見的方法是定義一個抽象接口類。在那裏聲明的虛函數充當回調函數,並可以調用派生實現類中定義的任意代碼。

那些喜歡通用和函數式編程的人會改爲定義一個重載operator()的類 - 通常稱爲「函子」或「函數對象」。這樣的東西可以稱爲功能,但也可以帶有狀態。這種模式在整個標準庫中被廣泛使用,例如爲容器提供自定義分配器,爲排序算法提供自定義比較器。

這兩種方法都可以更方便地爲回調提供額外的信息 - 可能是調用成員函數的對象或用於比較函數參數的值。在C語言中,任何這樣的「用戶」狀態都必須通過回調中的額外參數來傳遞 - 如果回調機制的設計者忽略了這一點,那麼使用它可能會非常棘手。

C++ 11提供了一些方便的新方法來處理函子。 std::function是一種多態類型,它可以表示任何類型的函數類型的東西,並且如果您不希望在函數類型上進行模板化,則它是用於回調的良好類型。std::bind和lambda表達式是從一些任意代碼段定義函數的簡單方法。

的回調可以作爲一個簡單的替代多態性與泛型編程

斷章取義,不使有很大的意義。我想這意味着,如果您使用函數指針進行回調,則不需要定義任何抽象接口或函子。如果他們認爲多態性和通用性很複雜,有些人可能認爲這更簡單。但是它也帶來了它自己的複雜性,因爲沒有類型安全的方法將任意用戶數據傳遞給回調函數,就像C++方法一樣。

1

一個例子是qsort。您傳遞迴調函數以確定元素的排序。

我不確定回調比多態性更簡單,除非您在C中編碼了50年,並且從未使用過OOP。多態是一種中心OOP概念,不僅與C++相關,而且並非所有的OOP語言都有函數指針。但是,在純C中,你可以通過函數指針模擬多態。

+0

在不希望創建整個類作爲某些函數的委託的情況下,回調更簡單。我特別喜歡蘋果塊,這是他們真正閃耀的地方。 – 2012-03-06 14:11:59

+0

@ RichardJ.RossIII:沒錯,但是現在「創建一個完整的類作爲委託」就像寫一個lambda表達式一樣簡單 - 並且比創建一個完整的函數成爲委託更簡單。 – 2012-03-06 14:14:52

+0

@MikeSeymour'創建lambda'在標準的C/C++中是不可能的,這就是這個問題的關鍵。這就是爲什麼我提出了蘋果塊,因爲它們大多數都是來自C#的匿名委託,但它們是幕後的函數指針。 – 2012-03-06 14:16:10

0

我想知道回調函數在哪裏實際使用?

GUI programming。在Windows平臺上,當您想要創建窗口時,將回調函數傳遞給控制窗口行爲的系統。當然,高級框架會隱藏這種行爲,但它仍然存在。

Multithreading,異步IO,當你想讓系統進程使用用戶定義的函數(例如qsort)時。

基本上,如果您想要製作應該與多種語言交互的可擴展代碼,就不能沒有回調。

0

回調函數的含義是:函數不被程序員在代碼中的任何地方調用,但是從外部源調用。通常,這意味着操作系統。

  • 如果調用該函數的外部源是硬件,則被調用的函數被命名爲「中斷服務程序」。
  • 如果外部來源是軟件,被調用的函數被命名爲「回調函數」。

例如,在Windows編程中會發現大量的回調函數。每個圖形窗口都有一個回調函數「WindowProc」,它接收來自操作系統的窗口上發生的每個動作的調用。然後由應用程序員編寫回調函數並處理他們感興趣的事件。他們不感興趣的事件被傳遞給默認的處理程序(或基類,如果您願意的話)。

如果程序員想要在窗口上單擊鼠標時發生某些特殊情況,可以通過用自己的代碼替換默認行爲來覆蓋鼠標點擊的默認行爲。這樣你就可以實現多態,即使在C程序中也是如此。從Windows編程

實施例:

LRESULT CALLBACK WindowProc (HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) 
{ 
    switch(umsg) 
    { 
    case WM_LBUTTONDOWN: 

     do_something(); /* overwrite default behavior of a left mouse button click */ 

     break; /* break = and then execute the default behavior afterwards */ 

    case WM_RBUTTONDOWN: 

     do_something(); /* overwrite default behavior of a right mouse button click */ 

     return 0; /* overwrite the default behavior entirely */ 

    }  


    // execute default behavior for the event: 
    return DefWindowProc(hwnd, umsg, wparam, lparam); 
} 

從switch語句的break類似於普通傳承:執行繼承的功能,然後將基類的功能。雖然return 0;與多態/虛擬繼承類似,但由於繼承對象完全覆蓋默認行爲。


旁註:由於一個回調函數從外部來源,而不是程序本身調用,有些木訥的編譯器將會使該函數從來沒有所謂的假設,因此執行危險的優化。例如,如果上面的WindowProc修改了全局變量「flag」,main()中的代碼依賴於「flag」,編譯器可能會優化掉main()中的代碼,因爲它認爲「flag」是從來沒有使用過任

爲了避免這種不正確的編譯器行爲,最好總是將所有在回調函數和程序其餘部分之間共享的變量聲明爲volatile,這將阻止對該變量的所有優化。