zoukankan      html  css  js  c++  java
  • C++中实现回调机制的几种方式(一共三种方法,另加三种)

    (1)Callback方式
    Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型。

    比如下面的示例代码,我们在Download完成时需要触发一个通知外面的事件:

    typedef void (__stdcall *DownloadCallback)(const char* pURL, bool bOK);
    void DownloadFile(const char* pURL, DownloadCallback callback)
    {
        cout << "downloading: " << pURL << "" << endl;
        callback(pURL, true);
    }
    void __stdcall OnDownloadFinished(const char* pURL, bool bOK)
    {
        cout << "OnDownloadFinished, URL:" << pURL << "    status:" << bOK << endl;
    }


    (2)Sink方式
    Sink的本质是你按照对方要求实现一个C++接口,然后把你实现的接口设置给对方,对方需要触发事件时调用该接口, COM中连接点就是居于这种方式。

    上面下载文件的需求,如果用Sink实现,代码如下:

    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)Delegate方式
    Delegate的本质是设置成员函数指针给对方,然后让对方在需要触发事件时调用。
    C#中用Delegate的方式实现Event,让C++程序员很是羡慕,C++中因为语言本身的关系,要实现Delegate还是很麻烦的。
    上面的例子我们用Delegate的方式实现如下: 

    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;
        }
    };


    可以看到Delegate的方式代码量比上面其他2种方式大多了,并且我们上面是固定参数数量和类型的实现方式,如果要实现可变参数,要更加麻烦的多。
    可变参数的方式可以参考这2种实现:
    Yet Another C#-style Delegate Class in Standard C++
    Member Function Pointers and the Fastest Possible C++ Delegates


    我们可以用下面的代码测试我们上面的实现:

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

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

        CMyFile f1;
        f1.download();

        CMyFileEx ff;
        ff.download();

        system("pause");

        return 0;
    }



    最后简单比较下上面3种实现回调的方法:
    第一种Callback的方法是面向过程的,使用简单而且灵活,正如C语言本身。
    第二种Sink的方法是面向对象的,在C++里使用较多, 可以在一个Sink里封装一组回调接口,适用于一系列比较固定的回调事件。
    第三种Delegate的方法也是面向对象的,和Sink封装一组接口不同,Delegate的封装是以函数为单位,粒度比Sink更小更灵活。 

    你更倾向于用哪种方式来实现回调?

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


    楼主的文章放在08年以前发表还可以读读,技术在进步,楼主没有进步。介绍两种方式
    1.__event/__raise/__hook 机制(微软自己扩展),和C#的event同样用法。
    2.C++ 0x11支持的lambda表达式和function类,可以替代一切回调。 我现在基本用这种方法。

    3.如果不能使用c++11的话还可以是用boost中的function的吧

  • 相关阅读:
    微信小程序HTTPS
    微信商城-1简介
    va_list
    Event log c++ sample.
    EVENT LOGGING
    Analyze Program Runtime Stack
    unknow table alarmtemp error when drop database (mysql)
    This application has request the Runtime to terminate it in an unusual way.
    How to check if Visual Studio 2005 SP1 is installed
    SetUnhandledExceptionFilter
  • 原文地址:https://www.cnblogs.com/findumars/p/5786069.html
Copyright © 2011-2022 走看看