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、為什么使用回調函數

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

img

例子:

 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 }

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM