1. 程式人生 > >C++ 回撥函式理解

C++ 回撥函式理解

http://blog.csdn.net/clirus/article/details/50350519

程式設計中肯定會遇到在C++中使用回撥函式的情況。

但是為什麼要使用回撥函式呢?我們需要理解回撥函式設計原理

因為可以把呼叫者與被呼叫者分開。呼叫者不關心誰是被呼叫者,所有它需知道的,只是存在一個具有某種特定原型、某些限制條件(如返回值為int)的被呼叫函式。
如果想知道回撥函式在實際中有什麼作用,先假設有這樣一種情況,我們要編寫一個庫,它提供了某些排序演算法的實現,如氣泡排序、快速排序、shell排序、shake排序等等,但為使庫更加通用,不想在函式中嵌入排序邏輯,而讓使用者來實現相應的邏輯;或者,想讓庫可用於多種資料型別(int、float、string),此時,該怎麼辦呢?可以使用函式指標,並進行回撥。


回撥可用於通知機制,例如,有時要在程式中設定一個計時器,每到一定時間,程式會得到相應的通知,但通知機制的實現者對我們的程式一無所知。而此時,就需有一個特定原型的函式指標,用這個指標來進行回撥,來通知我們的程式事件已經發生。實際上,SetTimer() API使用了一個回撥函式來通知計時器,而且,萬一沒有提供回撥函式,它還會把一個訊息發往程式的訊息佇列。


另一個使用回撥機制的API函式是EnumWindow(),它列舉螢幕上所有的頂層視窗,為每個視窗呼叫一個程式提供的函式,並傳遞視窗的處理程式。如果被呼叫者返回一個值,就繼續進行迭代,否則,退出。EnumWindow()並不關心被呼叫者在何處,也不關心被呼叫者用它傳遞的處理程式做了什麼,它只關心返回值,因為基於返回值,它將繼續執行或退出。


不管怎麼說,回撥函式是繼續自C語言的,因而,在C++中,應只在與C程式碼建立介面,或與已有的回撥介面打交道時,才使用回撥函式。除了上述情況,在C++中應使用虛擬方法或函式符(functor),而不是回撥函式。

如果你不想封裝一個C介面來進行回撥,那必須通過C++的方式實現回撥。

回撥函式的原理不在贅述,無非就是傳入一個函式地址,然後在達到某種情況的時候,可以通過這個函式地址呼叫使用者(此使用者指回調函式的使用者)希望的函式介面,繼而傳遞引數,所以回撥函式的實現是比較簡單的,只需要將函式地址儲存,然後觸發呼叫即可。

那我們就說明一下在C++中回撥函式的使用。

在C++中,如何使用類的成員函式作為回撥函式入參呢?我們先理解一下C++靜態成員函式。

在C++中普通成員函式和靜態成員函式的區分如下:

1、靜態成員函式的地址可用普通函式指標儲存,而普通成員函式地址需要用 類成員函式指標來儲存。舉例如下: 

  1. class base{   
  2. staticint func1();   
  3. int func2();   
  4. };   

  1. int (*pf1)()=&base::func1;//普通的函式指標 
  2. int (base::*pf2)()=&base::func2;//成員函式指標 


2、靜態成員函式不可以呼叫類的非靜態成員。靜態成員函式不含this指標,而普通成員函式預設攜帶this指標引數。

有了以上區分,則我們可以知道只能 原則上只能是靜態成員函式作為回撥函式的入參,但是我們依舊可以做些變化,可以對成員函式的指標型別進行強轉。

廢話不多說,下面是C++呼叫C的回撥函式的例子

1、非靜態成員函式作為回撥函式引數例子

非靜態成員函式作為回撥函式引數的天生弊端已經在上面對比中講清楚,所以我們回撥函式的設計必須要能夠滿足非靜態成員函式的情況。

回撥函式:(我們採用C方式實現回撥函式,和C++並沒有什麼區別)

  1. typedefstruct
  2. {  
  3.     int aa;  
  4.     int bb;  
  5. }testMsgType;  
  6. // 回撥函式指標定義 void*用來儲存this指標
  7. typedefint (*ptestCB)( testMsgType *, void *);  
  8. ptestCB m_test; // 儲存回撥函式地址
  9. void* m_kk;// 儲存this指標
  10. // 回撥函式
  11. int setCBTest(ptestCB pf, void * kk)  
  12. {  
  13.     printf("wei....... set cb ok!!!\n");  
  14.     m_test = pf;  
  15.     m_kk = kk;  
  16.     return 0;  
  17. }  
  18. int cbfuc()  
  19. {  
  20.     printf("cb func !!!\n");  
  21.     stZigBeeMsg* type = new testMsgType;  
  22.     m_test(type,m_kk); // 呼叫回撥函式
  23.     return 0;  
  24. }  

回撥函式呼叫:

  1. Caa.h  
  2. class Caa  
  3. {  
  4. public:  
  5.      Caa();  
  6.     ~Caa();  
  7.     init();  
  8.     // 類的非靜態成員函式作為回撥函式引數
  9.     int onmycb(stZigBeeMsg *  p,void* kk);  
  10.     int dealCB(ptestCB* p);  
  11. }  


  1. Caa.c  
  2. Caa::Caa()  
  3. {  
  4.     init();  
  5. }  
  6. Caa::~Caa()  
  7. {  
  8. }  
  9. bool Caa::init()  
  10. {  
  11.     // 設定回撥函式
  12.     // 需要將成員函式的this指標傳入
  13.     // 需要將類成員函式地址強制轉換為回撥函式型別
  14.     setCBTest((ptestCB)&Caa::onmyevent,this);  
  15.     returntrue;  
  16. }  
  17. // 回撥函式
  18. int Caa::onmyevent(testMsgType *  p,void* aa)  
  19. {  
  20.     // 將回調回來的指標強制轉換為類指標,然後呼叫類的成員函式
  21.     ((CEventMgmt*)aa)->dealCB(p);  
  22.     return 0;  
  23. }  
  24. // 業務處理函式
  25. int Caa::dealCB(testMsgType* p)  
  26. {  
  27.       return 0;  
  28. }  


2、靜態成員函式作為回撥函式引數,非靜態類

靜態成員函式作為回撥函式引數時,因為靜態成員函式沒有this指標,所以回撥函式的設計比較簡單,不用包含void *來傳遞儲存this指標。

回撥函式:

  1. // 回撥函式指標定義 不用包含void *
  2. typedefint (*ptestCB)( stZigBeeMsg *);   
  3. // 儲存回撥函式地址
  4. ptestCB m_test;   
  5. int setCBTest(ptestCB pf)  
  6. {  
  7.     printf(" set cb ok!!!\n");  
  8.     m_test = pf;  
  9.     return 0;  
  10. }   
  11. int cbfuc()  
  12. {  
  13.     printf("cb func !!!\n");  
  14.     testMsgType* type = new testMsgType;  
  15.     m_test(type);  
  16.     return 0;  
  17. }  


回撥函式呼叫:

  1. Caa.h  
  2. class Caa  
  3. {  
  4. public:  
  5.      Caa();  
  6.     ~Caa();  
  7.     init();  
  8. private:  
  9.     //設定當前物件為回撥函式呼叫的物件 
  10.     void setCurClass()    
  11.     {   
  12.         spCB = this;   
  13.     }    
  14.     staticint onmycb(stZigBeeMsg *  p);  
  15.     int dealCB(ptestCB* p);  
  16. private:  
  17.     static Caa* spCB;//儲存回撥函式呼叫的物件
  18. }  

Caa.c

  1. Caa* Caa::spCB = NULL;  
  2. Caa::Caa()  
  3. {  
  4.     init();  
  5. }  
  6. Caa::~Caa()  
  7. {  
  8. }  
  9. bool Caa::init()  
  10. {  
  11.     // 將spCB設定為this供回撥使用
  12.     setCurClass();  
  13.     // 設定回撥函式
  14.     // 需要將成員函式的this指標傳入
  15.     // 需要將類成員函式地址強制轉換為回撥函式型別
  16.     setCBTest(&Caa::onmyevent);  
  17.     returntrue;  
  18. }  
  19. // 回撥函式
  20. int Caa::onmyevent(testMsgType *  p)  
  21. {  
  22.     // 將回調回來的指標強制轉換為類指標,然後呼叫類的成員函式
  23.     spCB->dealCB(p);  
  24.     return 0;  
  25. }  
  26. // 業務處理函式
  27. int Caa::dealCB(testMsgType* p)  
  28. {  
  29.     return 0;  
  30. }   


3、靜態成員函式作為回撥函式引數,採用單例設計模式

和上面的例子思路是一致的,上面採用一個類的靜態物件來做中間轉換,繼而呼叫非靜態成員函式。

那麼假如我設計類為單例模式,則直接可以在靜態成員函式回撥內部進行呼叫非靜態成員函式,如下:

先設定類為單例

  1. Caa * Caa::instance()  
  2. {  
  3.     static Caa *p;  
  4.     if (p == NULL)  
  5.     {  
  6.         p = new Caa;  
  7.     }  
  8.     

    相關推薦

    C++ 函式理解

    http://blog.csdn.net/clirus/article/details/50350519程式設計中肯定會遇到在C++中使用回撥函式的情況。但是為什麼要使用回撥函式呢?我們需要理解回撥函式設計原理因為可以把呼叫者與被呼叫者分開。呼叫者不關心誰是被呼叫者,所有它需

    C++函式的一點理解

    回撥函式是通過函式指標呼叫的函式:把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,就稱為回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。 通俗點說就是:在A類中

    C++函式的基本理解和使用

    回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響

    C++函式理解與使用

    一、回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。 回撥函式機制: 1

    函式理解(轉載)

     想要詳細理解回撥函式的內容還需要理解與javascript單執行緒和非同步相關知識。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <

    C++函式

    摘要:回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。

    轉:C++函式

         今天討論下C/C++中的回撥函式。      在理解“回撥函式”之前,首先討論下函式指標的概念。 函式指標 (1)概念:指標是一個變數,是用來指向記憶體地址的。一個程式執行時,所有和執行相關的物件都是需要載入到記憶體中,這就決定了程式執行時的任何物件都可以

    淺析c#函式用法

    最近剛接觸到回撥函式,網上找了找解釋,感覺不是很明白,在專案中看到了回撥函式的實際用法,那就是回撥函式的提供者並不呼叫這個函式,而是把這個函式地址作為引數,傳遞給自己呼叫的其它方法的引數。所以回撥函式是要呼叫的函式不知

    C++函式用法

    一回調函式 我們經常在C++設計時通過使用回撥函式可以使有些應用(如定時器事件回撥處理、用回撥函式記錄某操作進度等)變得非常方便和符合邏輯,那麼它的內在機制如何呢,怎麼定義呢?它和其它函式(比如鉤子函式)有何不同呢? 使用回撥函式實際上就是在呼叫某個函式(通常是API函式)時,將自己的一個函式(這個函式為回

    C++函式呼叫Java介面抽象函式

    專案中很多程式碼採用C++編寫,配置介面則採用BS結構,使用Java語言進行設定。因此需要實現Java呼叫C++編寫的函式庫(dll檔案或so檔案),採用的技術為JNI(Java Native Interface),對於常用的呼叫方式在《The Java Native In

    c++函式 callback

                                                                          C++中實現回撥機制的幾種方式(1)Callback方式 Callback的本質是設定一個函式指標進去,然後在需要需要觸發某個事件時

    關於nodejs中的函式理解

    Node的三個特點:單執行緒,非阻塞I/O,事件驅動。Node的程式設計思維就是,所有的都是非同步的,因此有了大量的回撥函式。 回撥函式,就是放在另外一個函式(如 parent)的引數列表中,作為引數傳遞給這個 parent,然後在 parent 函式體的某

    c++函式/ROS函式

    以下均是個人在實際耕碼的過程中遇到的問題和整理的結果,可能會有不對的地方,望各位指正與交流 ------------------------------------------------------------------我會有喵的--------------------

    js 函式理解總結

    <script type="application/javascript" language="JavaScript">function dosomething(damsg, callback) {                 callback();alert(damsg);if(typeo

    淺談C/C++函式(Callback)& 函式指標

    摘要:回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。

    C++函式跨模組

    #ifndef CONTROLLER_H #define CONTROLLER_H // // 檔名:Controller.h // 工程名:CppCallback // 開發環境:MacOS 10.13.1 Qt5.9.1 // 簡介:回撥函式使用 // 建

    淺談C++函式

      1.什麼是回撥函式?       回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,

    C語言之最好理解的通過函式指標作為引數實現函式

    1、函式指標回撥解釋 回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式 2、程式碼實現 #i

    C#中委託、事件和函式理解

    在C#中我們經常會碰到事件,尤其是在WPF或者WinForm中,窗體載入、或者點選一個按鈕,都會觸發事件。實際上,事件是對委託的封裝。如果不進行封裝,讓委託暴露給呼叫者,呼叫者就可以把委託變數重新引用到新的委託物件,也就刪除了當前要呼叫的方法列表;更糟糕的是,公共的委託成員打破了封裝不僅導致程式碼難以維護和除

    c函式與java中抽象函式,介面函式,抽象類簡單理解

    先了解c語言的回撥函式,舉個簡單例項步步深入,比如A程式提供給B程式使用,但是A想要呼叫B的程式碼,這樣各自不同功能由B實現即可。 例項1A: extern int get_B_data(); void A_data_handle(){ printf("%d\n",get