关于C++回调函数设计的思考

来源:互联网 发布:济南淘宝代运营公司 编辑:程序博客网 时间:2024/06/05 06:21

之前在接触到高性能异步事件驱动编程时,里面涉及到大量的回调,对于回调函数的实现在C语言中很统一,通过函数指针的方式来进行。将自己实现的回调函数地址赋值给回调函数指针。但是在C++中回调的方式可以多种多样。实际上,回调函数类似于设计模式中的策略模式,统一的接口但是有不同的实现。

1.多态方式实现回调函数

通过提出一个公共接口类,具体业务由子类执行,在回调绑定的时候只需要把子类对象的指针赋值给基类指针,通过基类指针调用公共接口就可以实现回调绑定了,具体看代码如下:

#include <iostream>using namespace std;class GameCharacter {public:    //non virtual interface    int healthValue() {        //doing something before calling the fucntion        int retVal = doHealthValue();        //doing something after calling the function        return retVal;    }private:    virtual int doHealthValue() const {        return 3;    }};class DeriveGameCharacter:public GameCharacter {public:    DeriveGameCharacter() {};    ~DeriveGameCharacter() {};private:    virtual int doHealthValue() const {        return 4;    }};int main(){    GameCharacter *b_class = new DeriveGameCharacter();    cout << b_class->healthValue() <<endl;    return 0;}

编译执行,输出结果为4,输出的是子类的方法,这个很正常,理解起来应该问题不大。

2.函数指针的方式实现回调逻辑

直接看代码吧:

#include <iostream>using namespace std;class GameCharacter;int defalutHealthCalc( const GameCharacter &gc ) {    return 2;}class GameCharacter {public:    typedef int ( *HealthCalcFunc ) (const GameCharacter& );    explicit GameCharacter( HealthCalcFunc hcf )        :health_func_( hcf ) {    }    int healthValue() const {        return health_func_( *this );    }private:    HealthCalcFunc health_func_;};class EvilBadGuy:public GameCharacter {public:    explicit EvilBadGuy( HealthCalcFunc hcf ):        GameCharacter(hcf) {        }};int loseHealthQuickly( const GameCharacter &gc ) {    return 4;}int main(){    GameCharacter b_class(defalutHealthCalc);    EvilBadGuy d_class(loseHealthQuickly);    cout<< b_class.healthValue() <<endl;    cout<< d_class.healthValue() <<endl;}

输出结果:2 4
可以从main函数里面看出,将函数指针进行赋值,通过函数指针具体指向的函数实现不同来执行具体的业务逻辑。但是这种多态的局限性也很明显。函数在外部定义,通过传对象引用来确定具体执行业务逻辑,在C语言里面可以这么干,但是在C++里面,这种做法略显笨拙,在C++里面如果希望动态绑定某个类的成员函数该怎么办呢。

3.基于模板的做法

下面这种做法是我在看libev C++代码的时候偶然发现的,感觉比较高大上,于是就试着自己写写看了。

#include <iostream>using namespace std;class MyClass{    public:        MyClass(){}        template<class T , void (T::*method)()>        static void readCallback ( void *object ) {            (static_cast<T *>(object) ->*method)();        }};class Object {    public:        Object() {        }        void printHello() {            cout << "Hello" <<endl;        }    private:        MyClass a;};int main(){    Object b;    MyClass a;    //绑定回调函数    a.readCallback< Object, &Object::printHello>( &b );    return 0;}

利用函数模板这种方法进行回调绑定,以及对象成员函数来实现具体的回调逻辑。

4. 基于tr1::bind的回调

C++ std::tr1提供一个神奇的方式来代替函数指针,对函数指针进行抽象(C++在很多层面上都有抽象,将C语言的裸指针抽象为智能指针,将函数指针抽象为函数指针对象)std::tr1::bind。如何声明一个函数指针对象呢,具体代码如下:

std::tr1::function<float ( int,float )>

其中括号“()”外围的float代表函数的返回值,括号里面的代表函数的参数,实际上这个函数对象等价于函数指针 float *func(int, float),另外一个神奇的调用就是std::tr1::bind()这个函数是干什么呢?这个函数可以做函数对象之间的映射,举一个简单的例子:

#include <tr1/functional>#include <iostream>using namespace std;void function2(int a , int b , int c ) {    cout << a <<endl;    cout << b <<endl;    cout << c <<endl;}int main(){    tr1::function<void (int)> function1;    function1 = tr1::bind( function2 , tr1::placeholders::_1, 2 , 3 );    function1(1);    return 0;}

运行结果:1 2 3
这里的占位符等同于function1中的第几个参数,function1(_1)->function2(_1,2,3)即做到了从function1到function2的映射。这就是bind干的事情,于是有了这样一种函数绑定映射功能之后,我们就可以修改我们的策略模式了,具体代码如下:

#include <iostream>#include <tr1/functional>using namespace std;class GameCharacter;int defaultHealthCalc( const GameCharacter &gc );class GameCharacter {public:    typedef std::tr1::function<float (const GameCharacter&)> HealthCalcFunc;    explicit GameCharacter( HealthCalcFunc hcf = defaultHealthCalc )        :health_func_(hcf) {}    float healthValue() const {        return health_func_(*this);    }private:    HealthCalcFunc health_func_;};//计算方法,策略模式的不同实现class GameLevel {public:    GameLevel(int a ):a_(a){}    ~GameLevel(){}    float health( const GameCharacter & ) const {        cout << a_ <<endl;        return 4.2;    }private:    int a_;};class EvilBadGuy:public GameCharacter {public:    explicit EvilBadGuy( HealthCalcFunc hcf = defaultHealthCalc ):        GameCharacter(hcf) {        } };int main() {    GameLevel level(3);    EvilBadGuy ebg( std::tr1::bind(&GameLevel::health,                 level, std::tr1::placeholders::_1) );    cout << ebg.healthValue() <<endl;}

这里将std::tr1::function<float (const GameCharacter&)> 映射到float health( const GameCharacter & )由于health是类成员函数,隐含的传递了this指针,所以在bind的时候需要这么写
std::tr1::bind(&GameLevel::health, level, std::tr1::placeholders::_1)
具体调用的时候代码是这个:health_func_(*this)

基于bind的回调可以绑定的回调函数类型更多,灵活性也更好。

0 0
原创粉丝点击