关于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的回调可以绑定的回调函数类型更多,灵活性也更好。
- 关于C++回调函数设计的思考
- 关于c和java中回调函数的说明
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数- -
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- 关于回调函数
- java 克隆的浅拷贝与深拷贝
- Asp.net范例:amCharts(.net版)实现图形报表
- 【已解决】:让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap
- Discuz门户keywords和description不能正确显示
- BestCoder Round #53 (div.2)(hdu5422,hdu5423,hdu5424)
- 关于C++回调函数设计的思考
- 编写高质量Objective-C代码
- 关于json返回日期格式化的解决方案
- Eclipse Mars Release (4.5.0)下安装编译Spark2.7.1
- [Bluetooth Core V4.2] VOL2, PartB, 6 Packets
- oh-my-zsh and auto-fu.zsh
- Android开源项目分类汇总
- iOS使用AVCaptureSession自定义相机
- 编程之美 3.2 TelPhone