1. 程式人生 > >c++回撥機制全解:定義原型,註冊,回撥,封裝.

c++回撥機制全解:定義原型,註冊,回撥,封裝.

模組A呼叫模組B,但在B中,需要反過來呼叫到A的函式a(),則a()稱為回撥函式,在B中需要做的事:1.約定回撥函式原型.2.定義註冊回撥函式在A中需要:1.定義回撥函式2.呼叫B的註冊回撥函式使用回撥函式的幾個步驟:1.在B中,約定藉口規範,定義回撥函式a()的原型這裡回撥函式原型的定義最好遵循typedef void (*SCT_XXX)(LPVOID lp, const CBParamStruct& cbNode); SCT_XXX         -回撥函式名稱lp                    -回撥上下文CBParamStruct -回撥引數,一般由於要回調的引數不止一個,所以定義一個結構體比較方便。
2.在B中,定義註冊回撥函式void RCF_XXX(SCT_XXX pfn, LPVOID lp);
RCF_XXX -註冊函式名pfn          -回撥函式名稱(是指標)lp            -是回撥上下文. 一般在A模組初始化完B模組後呼叫,將A模組中定義的回撥函式地址賦值給pfn,lp賦值為this。

3.在A中,定義回撥函式回撥函式宣告成靜態的,static void  CF_XXX(LPVOID lp, const CBParamStruct& cbNode); 函式的引數必須與B模組中回撥函式原型的引數保持一致。在A中初始化B模組時,呼叫B的註冊函式將模組A中宣告的回撥函式CF_XXX的地址傳給pfn,即pfn=CF_XXX;(函式名稱CF_XXX其實是個指標,指向回撥函式的地址) 。然後lp一般為this.但若作為類,C++的類成員函式不能像普通函式那樣用於回撥,因為每個成員函式都需要有一個物件例項去呼叫它。通常情況下,要實現成員函式作為回撥函式,常用的方法就是把該成員函式設計為靜態成員函式,但這樣做有一個缺點,就是會破壞類的結構性,因為靜態成員函式只能訪問該類的靜態成員變數和靜態成員函式,不能訪問非靜態的,要解決這個問題,需要把物件例項的指標或引用做為引數傳給它。利用回撥代理類,可以簡單的實現非靜態函式的回撥。程式碼:
#ifndef CCALLBACKPROXY_H
#define CCALLBACKPROXY_H
template<typename Tobject, typename Tparam>
class CCallbackProxy
{
    //約定回撥函式原型
    typedef void (Tobject::*CbFun)(Tparam*);
public:
   //此處相當於註冊函式 
    void Set(Tobject *pInstance, CbFun pFun);
    bool Exec(Tparam* pParam);
private:
    CbFun       pCbFun;     //回撥函式指標
    Tobject*    m_pInstance;    //呼叫物件
};
#if 1
//設定呼叫物件及其回撥函式
template<typename Tobject, typename Tparam>
void CCallbackProxy<Tobject, Tparam>::Set(Tobject *pInstance , CbFun pFun)
{
    m_pInstance = pInstance;
    pCbFun = pFun;
}
//呼叫回撥函式
template<typename Tobject, typename Tparam>
bool CCallbackProxy<Tobject, Tparam>::Exec(Tparam* pParam)
{
    (m_pInstance->*pCbFun)(pParam);
    return true;
}
#endif
#endif // CCALLBACKPROXY_H
///non cpp file

///main.cpp
#include <QCoreApplication>
#include "ccallbackproxy.h"
class CTest
{
public:
    CTest(int nNum)
    {
        m_nSum = nNum;
    }
    void CbPrintSum(int *pnAddNum)
    {
        printf("The Sum is %d\n", m_nSum+*pnAddNum);
    }
private:
    int m_nSum;
};
int main(int argc,  char* argv[])
{
    CCallbackProxy<CTest, int> CbProxy;
    CTest TestInstance(20);
    //註冊
    CbProxy.Set(&TestInstance, &CTest::CbPrintSum);
    int nNum = 1000;
    CbProxy.Exec(&nNum);
    return 0;
}