[转]C/C++实现回调机制的几种方式(回调、槽、代理)

转自:https://www.jianshu.com/p/4f907bba6d5f

(1)Callback方式(回调)

Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型。比如下面的示例代码,我们在Download完成时需要触发一个通知外面的事件:

#include <iostream>

typedef void (__stdcall *DownloadCallback)(const char *pURL,bool OK);

void DownLoadFile(const char *pURL,DownloadCallback callback)
{

    std::cout<<"downloading..."<<pURL<<""<<std::endl;
    callback(pURL,true);
}

void __stdcall onDownloadFinished(const char* pURL,bool bOK)
{
    std::cout<<"onDownloadFinished..."<<pURL<<"   status:"<<bOK<<std::endl;
}

void main()
{
    DownLoadFile("http://wwww.baidu.com",onDownloadFinished);

    system("pause");
}

(2)Sink方式(槽)

Sink的本质是你按照对方要求实现一个C++接口,然后把你实现的接口设置给对方,对方需要触发事件时调用该接口, COM中连接点就是居于这种方式。上面下载文件的需求,如果用Sink实现,代码如下:

//2. 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)
    {
        std::cout<<"downloading..."<<pURL<<""<<std::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)
    {
        std::cout<<"onDownloadFinished..."<<pURL<<"   status:"<<bOK<<std::endl;
    }
};

void main()
{
    CMyFile *file = new CMyFile();
    file->download();

    system("pause");
}

小结:从上面的代码中可以看出,IDownloadSink 接口是一种约定,CMyDownloader 是接口的使用者,CMyFile是接口的实现者。他们两者之间是通过IDownloadSink 进行解耦的,使他们可以专注于自己的实现,接口的使用者CMyDownloader只管怎么使用接口的,接口怎么实现他不必关心;而接口的实现者IDownloadSink 他只管实现接口,接口怎么去用他不关心。维系两者关系的就是接口约定IDownloadSink 。

(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更小更灵活。

原文地址:https://www.cnblogs.com/lyggqm/p/12010005.html

时间: 2024-10-02 14:16:40

[转]C/C++实现回调机制的几种方式(回调、槽、代理)的相关文章

C++中实现回调机制的几种方式(一共三种方法,另加三种)

(1)Callback方式Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型. 比如下面的示例代码,我们在Download完成时需要触发一个通知外面的事件: typedef void (__stdcall *DownloadCallback)(const char* pURL, bool bOK);void DownloadFile(const char* pURL, DownloadCallback call

c++回调lua的几种方式

第一种,通过函数名查找,每次都要查效率偏低 //全局表中找到函数luaL_getglobal(L, "OnTimer");//调用 lua_call(...); 第二种,c++提供接口由脚本注册回调函数,需要管理lua_ref的生命周期,管理不妥会有内存泄露 //c++提供lua接口,接受传递过来的lua函数int addTimer(lua_state* L) { int delay = lua_tonumer(L); int callback = luaL_ref(L, LUA_RE

android回调接口的两种方式

熟悉MS-Windows和X Windows事件驱动设计模式的开发人员,通常是把一个方法的指针传递给事件源,当某一事件发生时来调用这个方法(也称为“回调”).Java的面向对象的模型目前不支持方法指针,似乎不能使用这种方便的机制. 但是java本身拥有interface,我们可以用interface实现相同的效果.在android中,如果我们需要对一个button的点击事件往外传,可以有两种实现方式. 第一种方式的大体步骤是: 1.定义一个回调接口,用来捕捉和“存放”点击事件. public i

深入浅出: Java回调机制(异步)

Writer      :BYSocket(泥沙砖瓦浆木匠) 什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道"就一个回调-".此时千万个草泥马飞奔而过(逃 哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉.不妨总结总结. 一.什么是回调 回调,回调.要先有调用,才有调用者和被调用者之间的回调.所以在百度百科中是这样的: 软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用.回调和异步调用. 回调是一种特殊的调用,至于三种方式也有点不同. 1.同步

【Android与IOS开发对比系列】之 回调机制

[Android与IOS开发对比系列]之 回调机制 Android和IOS的回调的实现原理基本相同,只是具体命名不同而已. 本文将总结 IOS的Delegate和block, Android的interface和Handler. IOS 协议 委托Delegate是协议的一种,通过@protocol声明.委托类的作用,一是传值,二是传事件. 举个例子: 要实现选相册图片, 依据图: C类是委托类,定义了一个传值方法. A类显示一个图片,点击按钮,进入B类选取图片. 当在B类选完图片,将实现met

Java回调机制研究

1. 什么是回调函数 回调函数,顾名思义,用于回调的函数.回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数.回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机.回调函数包含下面几个特性: (1)属于工作流的一个部分: (2)必须按照工作流指定的调用约定来申明(定义): (3)他的调用时机由工作流决定,回调函数的实现者不能直接调用回调函数来实现工作流的功能: 2.  回调机制 回调机制是一种常见的设计模式,它把工作流内的某个供,按照约定的接口暴露给外部使用者,为

全面剖析Smarty缓存机制一[三种缓存方式]

今天主要全面总结下Smarty模板引擎中强大的缓存机制,缓存机制有效减少了系统对服务器的压力,而这也是很多开发者喜欢Smarty的原因之一,由于篇幅较大,便于博友阅读,这篇文章将剖析Smarty缓存的几种方式,下篇文章着重讲解下设置缓存及清除缓存的技巧方法(其中包含缓存集合方法). 一.Smarty缓存的几种方式缓存机制中,分为全局缓存.部分缓存.局部缓存三种方式,后面会一一讲述,下面是缓存设置前,Smarty类方法基本目录设置如下:$smarty->Smarty();$smarty->tem

Java回调机制

以前不理解什么叫回调,天天听人家说加一个回调方法啥的,心里想我草,什么叫回调方法啊?然后自己就在网上找啊找啊找,找了很多也不是很明白,现在知道了,所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法,这样子说你是不是有点晕晕的,其实我刚开始也是这样不理解,看了人家说比较经典的回调方式: Class A实现接口CallBack callback——背景1 class A中包含一个class B的引用b ——背景2 class B有一个参数为call

java中的回调机制的理解(小例子)

这样的解释似乎还是比较难懂,这里举个简单的例子,程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序.程序员B要让a调用自己的程序b中的一个方法,于是,他通过a中的接口回调自己b中的方法.目的达到.在C/C++中,要用回调函数,被掉函数需要告诉调用者自己的指针地址,但在JAVA中没有指针,怎么办?我们可以通过接口(interface)来实现定义回调函数. 正常情况下开发人员使用已经定义好的API,这个过程叫Call.但是有时这样不能满足需求,就需要程序员注册自己的程序,然后让