C++回调机制的几种实现方式



Callback

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

typedef void (__stdcall *DownloadCallback)(const char* pURL, bool bOK);
void DownloadFile(const char* pURL, DownloadCallback pCallback)
{
    ……
    pCallback (pURL, true);
}
void __stdcall OnDownloadFinished(const char* pURL, bool bOK)
{
    ……
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Sink

Sink的本质是你按照对方要求实现一个C++接口,然后把你实现的接口设置给对方,对方需要触发事件时调用该接口, COM中连接点就是居于这种方式。还是以上面的Download为例(你调用对方的下载类实现下载功能):

/*对方要求的接口*/
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)
    {
        ……
        if(m_pSink != NULL)
            m_pSink->OnDownloadFinished(pURL, true);
    }

private:
    IDownloadSink* m_pSink;
};
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
/*你的实现*/
class CMyFile: public IDownloadSink
{
public:
    void Download()
    {
        CMyDownloader downloader(this);
        downloader.DownloadFile("www.baidu.com");
    }

    virtual void OnDownloadFinished(const char* pURL, bool bOK)
    {
        ……
    }
}
  • 15

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

/*代理函数管理*/
typedef vector<CDownloadDelegateBase*> CDownloadDelegates;
class CDownloadEvent
{
public:
    ~CDownloadEvent()
    {
        CDownloadDelegates::iterator it = m_arDelegates.begin();
        while (it != m_arDelegates.end())
        {
            delete *it;
            ++it;
        }
        m_arDelegates.clear();
    }

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

    void operator -= (CDownloadDelegateBase* p)
    {
        CDownloadDelegates::iterator it = remove(m_arDelegates.begin(), m_arDelegates.end(), p);
        while (it != m_arDelegates.end())
        {
            delete *it;
            ++it;
        }
        m_arDelegates.erase(it, m_arDelegates.end());
    }

    void operator()(const char* pURL, bool bOK)
    {
        CDownloadDelegates::iterator it = m_arDelegates.begin();
        while (it != m_arDelegates.end())
        {
            (*it)->Fire(pURL, bOK);
            ++it;
        }
    }
private:
    CDownloadDelegates m_arDelegates;
};

class CMyDownloaderEx
{
public:
    void DownloadFile(const char* pURL)
    {
        ……
        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)
    {
       ……
    }
};
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

可以看到Delegate的方式代码量比上面其他2种方式多多了,并且上面是固定参数数量和类型的实现方式,如果要实现可变参数,要更加麻烦的多,具体可参考:
http://www.codeproject.com/Articles/11464/Yet-Another-C-style-Delegate-Class-in-Standard-C
http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

std::function(since C++ 11, vs2010)

template< class R, class... Args >
class function<R(Args...)>
  
  
  • 1
  • 2
  • 1
  • 2

类模板std :: function是一个通用的多态函数包装器。std :: function的实例可以存储,复制和调用任何可调用的目标:函数、lambda表达式、绑定表达式或其他函数对象。

#include <functional>
#include <iostream>

struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};

void print_num(int i)
{
    std::cout << i << '\n';
}

int main()
{
    // store a free function
    std::function<void(int)> f1 = print_num;
    f1(-9);

    // store a lambda
    std::function<void()> f2 = []() { print_num(123); };
    f2();

    // store the result of a call to std::bind
    std::function<void()> f3 = std::bind(print_num, 12345);
    f3();

    // store a call to a member function
    std::function<void(const Foo&, int)> f4 = &Foo::print_add;
    Foo foo(13579);
    f4(foo, 1);
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

std::mem_fn (since C++ 11, vs2010)

template< class R, class T >
/*unspecified*/ mem_fn(R T::* pm);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...));
template< class R, class T, class... Args >
/*unspecified*/ mem_fn(R (T::* pm)(Args...) const);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) volatile);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) const volatile);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) &);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) const &);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) volatile &);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) const volatile &);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) &&);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) const &&);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) volatile &&);
template< class R, class T, class... Args > 
/*unspecified*/ mem_fn(R (T::* pm)(Args...) const volatile &&);
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

函数模板std :: mem_fn生成指向成员函数的指针的包装对象,它可以存储,复制和调用指向成员函数的指针。 在调用std :: mem_fn时,可以使用对象的引用和指针(包括智能指针)。

/* Use mem_fn to store and execute a member function:*/
#include <functional>
#include <iostream> 
struct Foo {  
    void display_greeting() {    
        std::cout << "Hello, world.\n";  
    }  
    void display_number(int i) {    
        std::cout << "number: " << i << '\n';  
    }
}; 

int main() 
{
    Foo foo;
    auto func_greet = std::mem_fn(&Foo::display_greeting);
    func_greet(foo);  
    auto func_display = std::mem_fn(&Foo::display_number);
    func_display(foo, 42);
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
#include <iostream>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
#include <algorithm> 
int main()
{
    /*Pass a member function to std::transform to create a sequence of numbers:*/
    std::vector<std::string> words = {"It", "is", "a", "test"};
    std::vector<std::unique_ptr<std::string>> words2;    
    words2.emplace_back(new std::string("another"));    
    words2.emplace_back(new std::string("test"));

    std::vector<std::size_t> lengths;    
    std::transform(words.begin(), 
                  words.end(),
                  std::back_inserter(lengths), 
    std::mem_fn(&std::string::size)); 
    // uses references to strings    

    std::transform(words2.begin(),                 words2.end(), 
    std::back_inserter(lengths),
    std::mem_fn(&std::string::size)); 
    // uses unique_ptr to strings    
    std::cout << "The string lengths are ";   

    for(auto n : lengths) 
        std::cout << n << ' ';    
    std::cout << '\n';
}

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, 
OutputIt d_first, UnaryOperation unary_op)
{
    while (first1 != last1) {
        *d_first++ = unary_op(*first1++);    
    }    
    return d_first;
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

小结

1) Callback方法是面向过程的,使用简单而且灵活,正如C语言本身;
2) Sink方法是面向对象的,在C++里使用较多, 可以在一个Sink里封装一组回调接口,适用于一系列比较固定的回调事件;
3) Delegate方法也是面向对象的,和Sink封装一组接口不同,Delegate的封装是以函数为单位,粒度比Sink更小更灵活;
4) std::function和std::bind组合使用也可以实现类似函数指针的功能,但却却比函数指针更加灵活,特别是函数指向类的非静态成员函数时(本质上讲全局函数和静态成员函数没有区别,使用方法上除了静态成员函数在引用时要在前面加域作用符classname::外,没有其它任何区别;事实上全局函数也有可能放入命名空间或者使用全局域作用符,例如 namespace::function() 或::function,这样不仅本质上相同,形势上也与静态成员函数一致了)。在Effective C++中ITEM35建议使用该方法实现Strategy模式,实际上也可以用于代替callback 函数模仿C#中的event对象,而这里只能实现一个函数,实际上如果function<>模板实现了operator+以后,再在 function对象中维护一个列表,便可以实现C#中的event特性,即Delegate。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值