1. 程式人生 > 其它 >C++回撥函式詳解

C++回撥函式詳解

1、函式指標

概念

一個程式執行時,所有和執行相關的資源都需要被載入到記憶體中,如果在程式中定義了一個函式,那麼在編譯時系統就會為這個函式程式碼分配一段儲存空間,這段儲存空間的首地址稱為這個函式的地址。而且函式名錶示的就是這個地址。既然是地址我們就可以定義一個指標變數來存放,這個指標變數就叫作函式指標變數,簡稱函式指標。

使用函式指標實現函式呼叫

 1 #include <iostream>
 2  3 typedef void (*PINVOKE)(const char *str);
 4  5 void Invoke(const char *str)
 6 {
 7     std::cout << str << std::endl;
8 } 9 10 int main() 11 { 12 PINVOKE fp = Invoke; 13 fp("Hello world."); 14 15 return 0; 16 }

說明

函式指標與函式宣告的唯一區別就是用指標名(*fp)代替了函式名Invoke,然後進行賦值fp = Invoke就可以進行函式指標的呼叫了。宣告函式指標時要求函式返回值型別、引數個數、引數型別等與已定義函式保持一致。注意,函式指標必須用括號括起來 void (*fp)(char *s)

2、回撥函式

概念

宣告並定義一個函式A,然後把函式A的指標作為引數傳入其他的函式(或系統)中,其他的函式(或系統)在執行時通過函式指標呼叫函式A

,這就是所謂的回撥函式。簡單來說:回撥函式就是一個通過函式指標呼叫的函式。

示例

 1 #include <iostream>
 2  3 typedef void (*CALLBACKFUN)(const char *str);
 4  5 void PrintText(CALLBACKFUN fp, const char *str)
 6 {
 7     fp(str);
 8 }
 9 10 void Invoke(const char *str)
11 {
12     std::cout << str << std::endl;
13
} 14 15 int main() 16 { 17 PrintText(Invoke, "Hello world."); 18 return 0; 19 }

類成員函式作為回撥函式

回撥函式是基於C - Windows SDK的技術,不是針對C++的,程式設計師可以將一個C函式直接作為回撥函式,但是如果試圖直接使用C++的成員函式作為回撥函式將發生錯誤,因為普通的C++成員函式都隱含一個傳遞函式作為引數,即this指標,C++通過向其他成員函式傳遞一個指向自身的指標來實現程式函式訪問C++資料成員。所以實現類成員函式作為回撥函式有兩種途徑:1、不使用成員函式(使用友元操作符friendC函式訪問類的資料成員);2、使用靜態成員函式。

例如:

 1 #include <iostream>
 2  3 class CPrintString
 4 {
 5 public:
 6     void PrintText(const char *str)
 7     {
 8         std::cout << str << std::endl;
 9     }
10     
11     static void SPrintText(void *pPs, const char *str)
12     {
13         CPrintString *pThis = static_cast<CPrintString *>(pPs);
14         if(NULL == pPs)
15         {
16             return;
17         }
18         pThis->PrintText(str);
19     }
20 };
21 22 typedef void (*PRINTTEXT)(void *pPs, const char *str);
23 24 void CallBackFun(void *pPs, const char *str, PRINTTEXT fp)
25 {
26     fp(pPs, str);
27 }
28 29 int main()
30 {
31     CPrintString obj;
32     CallBackFun((void *)&obj, "Hello world.", CPrintString::SPrintText);
33     
34     return 0;
35 }

3、為什麼使用回撥函式

一般情況下,回撥函式能被普通函式替換,但回撥函式最重要的作用是解耦,在這一特點上普通函式代替不了回撥函式。

例子:

 1 #include <stdio.h>
 2 #include <softwareLib.h>     // 包含 Library Function 所在讀得 Software library 庫的標頭檔案
 3  4 int Callback()              // Callback Function
 5 {
 6     // TODO
 7     return 0;
 8 }
 9 10 int main()                  // Main program
11 {
12     // TODO
13     Library(Callback);
14     // TODO
15     return 0;
16 }

乍一看,回撥似乎只是函式間的呼叫,和普通函式呼叫沒啥區別,但仔細一看,可以發現兩者之間的一個關鍵的不同:在回撥中,主程式把回撥函式像引數一樣傳入庫函式。這樣一來,只要我們改變傳進庫函式的引數,就可以實現不同的功能,並且絲毫不需要修改庫函式的實現,這就是解耦。再仔細看看,主函式和回撥函式是在同一層的,而庫函式在另外一層,一般情況下庫函式對開發人員並不可見,庫函式的實現一般不會被修改,也就是說不能通過修改庫函式讓庫函式呼叫普通函式那樣實現,那就只能通過傳入不同的回撥函數了,這在企業開發中非常常見。