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

作者:袁晓辉(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 <typename ReturnType = void , typename ArgsType = void*>

     class Delegate

     {

     public:

         /*

         *构造函数

         *用于包装一个对象的成员函数

         * object 成员函数所属的对象指针

         * function 成员函数的指针

         */

          template<class ObjectType>

              Delegate(ObjectType* object,  ReturnType(ObjectType::*function)(ArgsType))

         {

             

         }

         /*

         *构造函数

         *用于包装一个全局函数/static函数

         * function 函数指针

         */

         Delegate(ReturnType(*function)(ArgsType))

         {

             

         }

};

}

Delegate有两个模板参数ReturnTypeArgsType,第一个指定被代理封装的函数(全局函数/类静态方法或类非成员方法)的返回值类型,第二个指定这个函数的参数类型。注意,由于C++模板只能有固定个数的参数,所以Delegate必须对所封装的函数的参数个数加以限制,也就是说Delegate参数的个数必须固定。既然个数必须固定,那就一个好了,反正我们可以传递结构作参数:)

Delegate有两个重载的构造函数。第二个是针对全局函数或类静态(static)函数的,只有一个参数,就是把ReturnType作为返回值且带一个ArgsType类型参数的函数的指针(ReturnType(*function)(ArgsType) 注意C风格的函数指针的定义方法)。第一个构造函数是针对类的非静态方法(成员方法)的,它本身也是“模板函数”,模板参数指定被它封装的成员方法所属的类。两个参数,第一个为对象指针,第二个为成员方法的指针。比如有如下类:

class C

{

public:

     int M (double param)

     {

         ......

         return ......;

     }

    

};

且有一个类C的实例对象objc

C objc;

那么,对于ojbcM方法可以使用下面的DelegateM进行封装:

Delegate<int, double> 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 <typename ReturnType = void, typename ArgsType = void*, bool MultiCast = true>

     class Event

     {

     public:

         /*

         *该事件处理函数所对应的代理类型

         */

         typedef Delegate<ReturnType, ArgsType> EventHandler;

     public:

         /*

         *构造函数

         */

         Event()

         {

         }

        

};

}

它有三个模板参数,分别为该事件代理(handler)的返回值类型、参数类型和一个标志该事件是否为多播(multicast)的bool值。前两个参数是说明可以用来“订阅”(下面会说明)该事件的Delegate的函数(全局/static或类成员)原型。所谓的“多播”指的是一个事件可以有多个订阅的代理,也就是说当这个事件被激发时,可能会有多个函数被调用,MultiCast的默认值为true,即允许多播。

一旦定义了一个事件,就可以调用它的+=操作符来把一个合适类型的Delegate对象订阅到改事件。-=运算符可以用来取消一个已经订阅的Delegate对象。订阅到某个事件的代理会在该事件被激发是被调用,该代理被从这个事件取消订阅后就不会再被该事件调用了。

比如有如下Event类型:

typedef CppEvent::Event<void, size_t> BalanceChanged;

和一个该类型的Event

BalanceChanged OnBalanceChanged;

可以用如下的方法把一个全局函数代理订阅到该事件:

OnBalanceChanged += Delegate<void, size_t> (OnTomsAccountBalanceChanged);

其中OnTomsAccountBalanceChanged的原型如下:

void OnTomsAccountBalanceChanged(size_t balance)

{

    

}

这样,在OnBalanceChanged被激发时OnTomsAccountBalanceChanged函数就会被调用。激发一个事件可以有下面两种方法:

调用EventInvoke()方法

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<bool, BalanceEventArgs&, false> BalanceChanging;

         //定义"帐户已改变"事件

         typedef CppEvent::Event<void, size_t> 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<bool ,Account::BalanceEventArgs&>(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 ? "操作成功!" : "操作失败!") <<endl << endl;

 

     cout << "即将执行 取款10000元 的操作" << endl;

     Success = TomsAccount.Withdraw(10000);

     cout << (Success ? "操作成功!" : "操作失败!") <<endl << endl;

 

     cout << "-Tom的要求,退订了他的手机短信通知-"<< endl;

     //退订了Tom的手机短信通知

     TomsAccount.OnBalanceChanged -= Account::BalanceChanged::EventHandler(&TomsPhone, MobilePhone::OnAccountBalanceChanged);

     //退订后TomsPhone将不再接收本事件

     cout << "即将执行 取款50元 的操作" << endl;

     Success = TomsAccount.Withdraw(50);

     cout << (Success ? "操作成功!" : "操作失败!") <<endl << endl;

 

}

 

运行该程序,可以看到输出如下:

 

即将执行 存款100 的操作

---系统日志 : Tom的帐户余额即将被改动, 存款100 此操作被允许---

---系统日志 : Tom的帐户余额被改动了,当前余额为: 100099*---

>>>> 手机短信 : 尊敬的用户,您的帐户余额发生了变化,当前余额为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参数哦。

原文地址:http://www.farproc.com/Article/ShowArticle.asp?ArticleID=198

原文地址:https://www.cnblogs.com/strinkbug/p/650937.html