C++事件(Event)机制的实现一例(转载)

C++事件(Event)机制的实现一例[源代码下载链接已修复] 作者:袁晓辉(farproc@gmail.com) 声明: 1、 本文为作者原创,如需转载请保持本文的完整性并注明出自 www.farproc.com 和 http://blog.csdn.net/uoyevoli. 2、 本文附件中的源代码你可以免费使用并无需注明出处。 用C++实现事件机制我以前写过一个小例子,但不是很完善,比如Event只能接受全局函数作为handler,类成员方法不可以,还有一个Event只能添加一个handler等……最近我的一个程序刚好要用到Event机制,所以我就抽了些时间,重新实现了一下。这个版本应该说是比较完善的,基本上和C#中的Event一样了。点击这里下载源代码。 要使用Event机制主要用到两个模板类:一个是Delegate,它实现了对C++函数指针的封装,当然,如上所述包括类成员函数的指针;另一个是Event,顾名思义它就是主角“事件”了。用以实现Event机制的所有类都被我“圈”在CppEvent命名空间里了,以免“污染”我们的global namespace。需要提醒的是,这个版本的实现中用到的C++的RTTI,所以用VC编译是别忘了加上/GR编译参数。 一 Delegate 先看一下Delegate类的原型: namespace CppEvent { … … /* *公开给程序使用的Delegate模板类 *ReturnType 函数返回值类型 *ArgsType 函数参数类型 */ template class Delegate { public: /* *构造函数 *用于包装一个对象的成员函数 * object 成员函数所属的对象指针 * function 成员函数的指针 */ template Delegate(ObjectType* object, ReturnType(ObjectType::*function)(ArgsType)) { … … } /* *构造函数 *用于包装一个全局函数/类static函数 * function 函数指针 */ Delegate(ReturnType(*function)(ArgsType)) { … … } }; } Delegate有两个模板参数ReturnType和ArgsType,第一个指定被代理封装的函数(全局函数/类静态方法或类非成员方法)的返回值类型,第二个指定这个函数的参数类型。注意,由于C++模板只能有固定个数的参数,所以Delegate必须对所封装的函数的参数个数加以限制,也就是说Delegate参数的个数必须固定。既然个数必须固定,那就一个好了,反正我们可以传递结构作参数:)。 Delegate有两个重载的构造函数。第二个是针对全局函数或类静态(static)函数的,只有一个参数,就是把ReturnType作为返回值且带一个ArgsType类型参数的函数的指针(ReturnType(*function)(ArgsType) 注意C风格的函数指针的定义方法)。第一个构造函数是针对类的非静态方法(成员方法)的,它本身也是“模板函数”,模板参数指定被它封装的成员方法所属的类。两个参数,第一个为对象指针,第二个为成员方法的指针。比如有如下类: class C { public: int M (double param) { ...... return ......; } … … }; 且有一个类C的实例对象objc: C objc; 那么,对于ojbc的M方法可以使用下面的DelegateM进行封装: Delegate DelegateM (&objc, C::M); 有了delegate,就可以调用它的Invoke方法来调用它所封装的函数,另外,Delegate重载了()操作符(operator)所以,你可以直接在Delegate对象后面加括号和参数进行调用了。比如int n =DelegateM.Invoke(0.2);或直接使用更简便的形式int n= DelegateM(0.2); 二 Event 看一下Event的原型: namespace CppEvent { /* *事件 模板类 */ template class Event { public: /* *该事件处理函数所对应的代理类型 */ typedef Delegate EventHandler; public: /* *构造函数 */ Event() { } … … }; … … } 它有三个模板参数,分别为该事件代理(handler)的返回值类型、参数类型和一个标志该事件是否为多播(multicast)的bool值。前两个参数是说明可以用来“订阅”(下面会说明)该事件的Delegate的函数(全局/static或类成员)原型。所谓的“多播”指的是一个事件可以有多个订阅的代理,也就是说当这个事件被激发时,可能会有多个函数被调用,MultiCast的默认值为true,即允许多播。 一旦定义了一个事件,就可以调用它的+=操作符来把一个合适类型的Delegate对象订阅到改事件。-=运算符可以用来取消一个已经订阅的Delegate对象。订阅到某个事件的代理会在该事件被激发是被调用,该代理被从这个事件取消订阅后就不会再被该事件调用了。 比如有如下Event类型: typedef CppEvent::Event BalanceChanged; 和一个该类型的Event: BalanceChanged OnBalanceChanged; 可以用如下的方法把一个全局函数代理订阅到该事件: OnBalanceChanged += Delegate (OnTomsAccountBalanceChanged); 其中OnTomsAccountBalanceChanged的原型如下: void OnTomsAccountBalanceChanged(size_t balance) { … … } 这样,在OnBalanceChanged被激发时OnTomsAccountBalanceChanged函数就会被调用。激发一个事件可以有下面两种方法: 调用Event的Invoke()方法 OnBalanceChanged.Invoke(100); 或直接利用Event的()操作符 OnBalanceChanged(100); 三 一个例子 下面我们以“银行帐户操作”为例子,来说明Event机制的使用。 首先是Account类: //Account.h #ifndef _ACCOUNT_H_ #define _ACCOUNT_H_ #include "../CppEvent/Event.h" namespace CppEventExampleAccount { class Account { public: //帐户操作 enum OperationType{DepositOp/*存款*/, WithdrawOp/*取款*/}; class BalanceEventArgs { public: BalanceEventArgs(OperationType operation, size_t ammount) :theOperation(operation) ,theAmmount(ammount) { }; virtual ~BalanceEventArgs() { }; OperationType Operation(void) const { return theOperation; } size_t Amount(void) const { return theAmmount; } private: OperationType theOperation; size_t theAmmount; }; public: //定义"帐户即将被改变"事件 //该事件接收一个BalanceEventArgs&参数,指定本次操作的具体信息 //第三个模板参数false指定该事件为"单播(Singlecast)",即只能有一个handler typedef CppEvent::Event BalanceChanging; //定义"帐户已改变"事件 typedef CppEvent::Event BalanceChanged; public: //定义两个事件对象 BalanceChanging OnBalanceChanging; BalanceChanged OnBalanceChanged; public: //构造函数 Account(size_t balance = 0) :Balance(balance) { } virtual ~Account() { } public: //查询余额 size_t GetBalance(void) const { return Balance; } //存款方法 //参数指定所存金额 bool Deposit(size_t amount) { bool ReturnValue = false; //激发"帐户即将被改变"事件,并接收其返回值 bool AllowOp = FireChangingEvent(DepositOp, amount); if(AllowOp)//如果允许改变 { //增加余额 Balance += amount; //激发"帐户已改变"事件 FireChangedEvent(); ReturnValue = true; } else { ReturnValue = false; } return ReturnValue; } //取款方法 //参数指定取款金额 bool Withdraw(size_t amount) { bool ReturnValue = false; if(Balance >= amount)//如果余额足够本次取款 { //激发"帐户即将被改变"事件,并接收其返回值 bool AllowOp = FireChangingEvent(WithdrawOp, amount); if(AllowOp) { //减少余额 Balance -= amount; //激发"帐户已改变"事件 FireChangedEvent(); ReturnValue = true; } } else//余额不足 { ReturnValue = false; } return ReturnValue; } protected: //激发"帐户即将被改变"事件 bool FireChangingEvent(OperationType operation, size_t amount) { bool ReturnValue = false; if(OnBalanceChanging != NULL) { BalanceEventArgs args(operation, amount); ReturnValue = OnBalanceChanging(args); } else //如果该事件没有handler则默认允许操作 { ReturnValue = true; } return ReturnValue; } //激发"帐户已改变"事件 void FireChangedEvent(void) { OnBalanceChanged(Balance); } private: //帐户余额 size_t Balance; }; } #endif 然后定义要订阅到Account两个事件的全局函数和MobilePhone类的成员函数: //Changing事件的handler bool OnTomsAccountBalanceChanging(Account::BalanceEventArgs& args) { TCHAR* OpName = args.Operation() == Account::DepositOp ? "存款" : "取款"; cout << "---系统日志 : Tom的帐户余额即将被改动, " << OpName << args.Amount() << "元"; cout << " 此操作被允许---" << endl; return true; } //Changed事件的handler void OnTomsAccountBalanceChanged(size_t balance) { cout << "---系统日志 : Tom的帐户余额被改动了,当前余额为: " << balance << "元*---" << endl; } //手机 class MobilePhone { public: void OnAccountBalanceChanged(size_t amount) { cout << ">>>> 手机短信 : 尊敬的用户,您的帐户余额发生了变化,当前余额为" << amount << "元,请注意!" << endl; } bool OnAccountBalanceChanging(Account::BalanceEventArgs&) { cout << "该方法不会执行" << endl; return false; } //...... //更多的成员和方法 //...... }; 然后定义类的实例对象,并编写main函数: //建立Tom的帐号 Account TomsAccount(99999); //Tom的手机 MobilePhone TomsPhone; int _tmain(int argc, _TCHAR* argv[]) { //为系统订阅Tom的帐号的"帐户即将被改动"事件 TomsAccount.OnBalanceChanging += Account::BalanceChanging::EventHandler(OnTomsAccountBalanceChanging); //上一行代码也可写为: ........+= Delegate (OnTomsAccountBalanceChanging); //为系统订阅Tom的帐号的"帐号已被改动"事件 TomsAccount.OnBalanceChanged += Account::BalanceChanged::EventHandler(OnTomsAccountBalanceChanged); //为Tom的手机订阅他的帐号的"帐户已被改动"事件 TomsAccount.OnBalanceChanged += Account::BalanceChanged::EventHandler(&TomsPhone, MobilePhone::OnAccountBalanceChanged); //为Tom的手机订阅Tom的帐号的"帐号已被改动"事件 //由于该事件为Singlecast,所以此订阅会失败 TomsAccount.OnBalanceChanging += Account::BalanceChanging::EventHandler(&TomsPhone, MobilePhone::OnAccountBalanceChanging); bool Success = false; //执行一些操作,激发一些事件.相应的事件handler函数会调用 cout << "即将执行 存款100元 的操作" << endl; Success = TomsAccount.Deposit(100); cout << (Success ? "操作成功!" : "操作失败!") << endl << endl; cout << "即将执行 存款5000元 的操作" << endl; Success = TomsAccount.Deposit(5000); cout << (Success ? "操作成功!" : "操作失败!") < >>> 手机短信 : 尊敬的用户,您的帐户余额发生了变化,当前余额为100099元,请注意! 操作成功! 即将执行 存款5000元 的操作 ---系统日志 : Tom的帐户余额即将被改动, 存款5000元 此操作被允许--- ---系统日志 : Tom的帐户余额被改动了,当前余额为: 105099元*--- >>>> 手机短信 : 尊敬的用户,您的帐户余额发生了变化,当前余额为105099元,请注意! 操作成功! 即将执行 取款10000元 的操作 ---系统日志 : Tom的帐户余额即将被改动, 取款10000元 此操作被允许--- ---系统日志 : Tom的帐户余额被改动了,当前余额为: 95099元*--- >>>> 手机短信 : 尊敬的用户,您的帐户余额发生了变化,当前余额为95099元,请注意! 操作成功! -应Tom的要求,退订了他的手机短信通知- 即将执行 取款50元 的操作 ---系统日志 : Tom的帐户余额即将被改动, 取款50元 此操作被允许--- ---系统日志 : Tom的帐户余额被改动了,当前余额为: 95049元*--- 操作成功! Press any key to continue 在main函数里对Account的实例TomsAccount进行操作时,订阅了指定事件的函数和方法会被调用,而当退订了某个函数或方法后,该函数或方法就不再在事件激发时被调用了。 以上代码在vc7.1中调试通过。最后再提醒一遍,别忘了VC的/GR参数哦。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值