コールバック機構(3つの方法の合計、プラス三種類)を実装するいくつかの方法でC ++

転送:http://www.cnblogs.com/weiym/archive/2012/08/28/2660053.html

簡単な紹介

コールバック関数は、通常、私たちはイベントが発生するとき、呼び出し側の呼び出し実装定義、二つの役割(発信者と実装者)で発生します。特定のイベントが動作を呼び出す必要が発生し、これらの行為の具体的な定義は、顧客によって行われたときに、プロバイダのフレームワークとして、それがプログラム全体のためのフレームワークを提供し、顧客が(別のイベントのためのさまざまな動作を選択することが実現します動的な呼び出し機能が実行されている)ので、呼び出し側は、特定の関数名を呼び出すことによって表示することができません。この問題を解決するには、単にすることができ、発信者にパラメータとして渡された関数の独自の定義を実装者、関数ポインタを通じて呼び出す呼び出します。あなたの機能が実装されていますが、他の誰か(またはシステム)機能により、実行時のパラメータは、コールバック関数と呼ばれる方法呼び出しによって渡されます。単にあなたが達成コールバック関数の間に機能するために他の人が、入れて実行します。

(1)コールバック方法

コールバック性質は、イベントをトリガするために必要な必要なときにそのようなWindowsのウィンドウのメッセージ処理機能として、メソッドを呼び出し、関数ポインタに設定されているこのタイプのものです。

void fun1(int a)
{
    cout << a << endl;
}

void fun2(int a)
{
    cout << a*a << endl;
}

void caller(void(*f)(int),int a)   //调用者提供接口,具体的实现由客户自己实现
{
    f(a);
}

int main()
{
    int a;
    cin >> a;
    if (a > 0)    //当a>0时,调用fun1
        caller(fun1, a);
    else          //当a<=0时,调用fun2
        caller(fun2, a);
    return 0;
}

(2)シンク方法

インターフェースがイベントをトリガする必要がある場合、シンクの本質はあなたがC ++インタフェースの他の要件に従って達成ということで、その後、あなたがお互いにインターフェース設定を実装し、COM接続ポイントは、このように生きている、お互いを呼び出します。

シンクを実装した場合、次のように、コードは、要件上記のファイルをダウンロードします。

class IDownloadSink
{
public:
    virtual void OnDownloadFinished(const char* pURL, bool bOK) = 0;
};
class CMyDownloader
{
public:
    CMyDownloader(IDownloadSink* pSink)
        :m_pSink(pSink)
    {
    }

    void DownloadFile(const char* pURL)
    {
        cout << "downloading: " << pURL << "" << endl;
        if(m_pSink != NULL)
        {
            m_pSink->OnDownloadFinished(pURL, true);
        }
    }

private:
    IDownloadSink* m_pSink;
};

class CMyFile: public IDownloadSink
{
public:
    void download()
    {
        CMyDownloader downloader(this);
        downloader.DownloadFile("www.baidu.com");
    }

    virtual void OnDownloadFinished(const char* pURL, bool bOK)
    {
        cout << "OnDownloadFinished, URL:" << pURL << "    status:" << bOK << endl;
    }
};

(3)委任方法

デリゲート本質は、相互にメンバ関数ポインタを設定し、その後、あなたは、トリガ・イベントが必要なときに呼び出す相手にさせることです。
C ++プログラマは非常にうらやましいようにC ++言語そのものとの関係は、委任を実現するため、イベントを達成するための委任方法を使用してC#のはまだ非常に面倒です。
我々上記の例は次のように委任を実装しました:

class CDownloadDelegateBase
{
public:
    virtual void Fire(const char* pURL, bool bOK) = 0;
};

template<typename O, typename T>
class CDownloadDelegate: public CDownloadDelegateBase
{
    typedef void (T::*Fun)(const char*, bool);
public:
    CDownloadDelegate(O* pObj = NULL, Fun pFun = NULL)
        :m_pFun(pFun), m_pObj(pObj)
    {
    }
    
    virtual void Fire(const char* pURL, bool bOK)
    {
        if(m_pFun != NULL
            && m_pObj != NULL)
        {
            (m_pObj->*m_pFun)(pURL, bOK);
        }
    }

private:
    Fun m_pFun;
    O* m_pObj;
};

template<typename O, typename T>
CDownloadDelegate<O,T>* MakeDelegate(O* pObject, void (T::*pFun)(const char* pURL, bool))
{
    return new CDownloadDelegate<O, T>(pObject, pFun);
}

class CDownloadEvent
{
public:
    ~CDownloadEvent()
    {
        vector<CDownloadDelegateBase*>::iterator itr = m_arDelegates.begin();
        while (itr != m_arDelegates.end())
        {
            delete *itr;
            ++itr;
        }
        m_arDelegates.clear();
    }

    void operator += (CDownloadDelegateBase* p)
    {
        m_arDelegates.push_back(p);
    }

    void operator -= (CDownloadDelegateBase* p)
    {
        ITR itr = remove(m_arDelegates.begin(), m_arDelegates.end(), p);

        ITR itrTemp = itr;
        while (itrTemp != m_arDelegates.end())
        {
            delete *itr;
            ++itr;
        }
        m_arDelegates.erase(itr, m_arDelegates.end());
    }

    void operator()(const char* pURL, bool bOK)
    {
        ITR itrTemp = m_arDelegates.begin();
        while (itrTemp != m_arDelegates.end())
        {
            (*itrTemp)->Fire(pURL, bOK);
            ++itrTemp;
        }
    }

private:
    vector<CDownloadDelegateBase*> m_arDelegates;
    typedef vector<CDownloadDelegateBase*>::iterator ITR;
};


class CMyDownloaderEx
{
public:
    void DownloadFile(const char* pURL)
    {
        cout << "downloading: " << pURL << "" << endl;
        downloadEvent(pURL, true);
    }

    CDownloadEvent downloadEvent;
};

class CMyFileEx
{
public:
    void download()
    {
        CMyDownloaderEx downloader;
        downloader.downloadEvent += MakeDelegate(this, &CMyFileEx::OnDownloadFinished);
        downloader.DownloadFile("www.baidu.com");
    }

    virtual void OnDownloadFinished(const char* pURL, bool bOK)
    {
        cout << "OnDownloadFinished, URL:" << pURL << "    status:" << bOK << endl;
    }
};

デリゲートは、道の最も上の他の二つよりも道にコードを見ることができ、あなたはより多くの悩みなどをする、可変パラメータを達成する必要がある場合は、私たちの上に固定パラメータの数と実装の種類は、です。
達成するために、これらの2つの方法の参照変数パラメータ:
委任ではまだ別のクラススタイルのC#C ++標準
メンバ関数ポインタと最速C ++代表

当社は、上記の私たちの実装をテストするには、次のコードを使用することができます。

int _tmain(int argc, _TCHAR* argv[])
{

    DownloadFile("www.baidu.com", OnDownloadFinished);

    CMyFile f1;
    f1.download();

    CMyFileEx ff;
    ff.download();

    system("pause");

    return 0;
}

最後に、コールバックメソッドの単純な比較は、上記の3つの種類を達成するために:
第一の方法は、C言語自体として、コールバックプロセス指向の、柔軟で使いやすいです。
第2の方法は、C ++でより使用、オブジェクト指向であるシンクは、比較的固定されたコールバックの一連のためにコールバック・インターフェース・シンクのグループを囲むことができます。
第三の方法は、デリゲートオブジェクト指向であり、インターフェイスの異なるセットがパッケージをシンク、パッケージが委任機能部であり、粒子径が小さく、シンクよりも柔軟です。

あなたは、コールバックを実装するためにどのような方法を好みますか?

おすすめ

転載: blog.csdn.net/QIJINGBO123/article/details/89150889