您的位置:首页 > 编程语言 > C语言/C++

杂货边角(9):C++实现事件委托机制

2018-01-18 16:37 3955 查看
在设计模式中,根据耦合紧密度可以得到排序为:继承 > 依赖 > 组合/聚合 > 委托。其实个人感觉,事件委托机制说白了就是一个函数指针数组,通过注册绑定的函数,然后在特定的事件发生后触发遍历执行所有注册的函数,机制其实和C++运行库glibc中
crtbegin.o
crtend.o
收集当前程序中所有全局对象的初始化函数和析构函数,用以执行遍历操作一致。

所以归根结底,事件委托机制的关键便是函数指针注册的实现。不过再次可惜的是,C++中还是没有实现像C#的delegate委托机制,所以又得动手操作了。

0. C#事件委托机制的使用示例

先把《大话设计模式》中关于事件委托机制的实例摆上,方便理解。

现在有一个cat类,mouse类,现在要实现cat发出叫声后, mouse就开始奔跑。这里信息的传递显然可以排除继承、依赖关系的使用,那么采用“关联关系”呢?这里需要明确的一点是,面向对象设计是模拟现实世界中的信息交互的,“关联关系”虽然相比于“继承”而言是耦合度较低的联系,但是“关联”在现实世界中对应的关系是双方认识,在C++类中体现为在对方类中存在一个自己类的实例对象。

但是此时思考的问题变成了,如果在现实世界中我们作为造物主,要实现“猫叫鼠奔”的场景,难道需要猫和老鼠彼此认识吗?显然作为信息流入口的只是cat.shout()动作,而非cat本身,故而这里需要的是一个比“关联”关系更弱耦合的联系,这便是事件委托机制。

这时更好的设计模式,便是直接在cat.shout()函数内部埋下委托事件,即一旦启用cat.shout()函数立即将该信息通过内部的注册表发送给周边的老鼠,启动mouse.run()。

先来看一下采用“关联关系”实现“猫叫鼠跑”场景的代码示意

class Cat
{
private string name;
public  Cat(string name)
{
this.name = name;
}

private IList<Mouse*> registedMouses;
public void Regist(Mouse* object)
{
registMouses.add(object);
}

public void Shout()
{
Console.WriteLine("喵喵我{0}来捉各位了", name);
if (registMouses.length() != 0)
{
foreach it in registMouses:
it->Run();
}
}
}

class Mouse
{
private string name;
public Mouse(string name)
{
this.name = name;
}

public void Run()
{
Console.WriteLine("老猫来了,{0}快跑!", name);
}
}


可以看到为了实现“猫叫鼠跑”,必须要让猫获取老鼠所有的信息,虽然猫的内部没有额外的处理老鼠信息的函数,但是直接将所有老鼠的信息直接暴露给猫,一旦这个猫想开外挂简直不要太容易。所以这种关联关系对于这个场景还是信息过量了。

再来看下用事件委托机制实现“猫叫鼠跑”场景

class cat
{
private string name;
public  Cat(string name)
{
this.name = name;
}

public delegate void CatShoutEventHandler(); //声明委托类型CatShoutEventHandler
/*这里需要注意的便是CatShoutEventHandler的声明类型为无参数、无返回值类型,这决定了改类注册
**机能注册的函数也只能是void xx(void)类型,原理可以参考此前的反射机制的注册机map声明
*/

public event CatShotEventHandler CatShout;  //声明事件CatShout对象

public void Shout()
{
Console.WriteLine("喵喵我{0}来捉各位了", name);
if (CatShout != NULL)
{
CatShout(); //当猫叫启动,如果CatShout注册机中有对象登记,则执行通知
}
}
}

class Mouse
{
private string name;
public Mouse(string name)
{
this.name = name;
}

public void Run()
{
Console.WriteLine("老猫来了,{0}快跑!", name);
}
}

static void Main(string[] args)
{
Cat cat = new Cat("Tom");
Mouse mouse1 = new Mouse("Jerry");
Mouse mouse1 = new Mouse("John");

cat.CatShout += new Cat.CatShoutEventHandler(mouse1.Run);
cat.CatShout += new Cat.CatShoutEventHandler(mouse2.Run);

cat.Shout();

Console.Read();
}


这样的设计看起来便是很舒服的,那么如何在C++实现事件委托机制呢?

1. C++实现事件委托机制

C++实现反射机制一样,事件委托机制的实现关键也是在注册函数指针上。

先来看下函数指针以及配合类如何使用的代码示例,摘自http://blog.csdn.net/y1196645376/article/details/51408114

#include <iostream>
#include <cstdio>

using namespace std;

void NormalFunc()
{
printf("这里是普通函数\n");
}

class A
{
public:
static void StaticFunc()
{
printf("这里是成员静态函数\n");
}
void MemberFunc()
{
printf("这里是成员非静态函数\n");
}
};
int main()
{
//普通函数
typedef void(*NormalFuncp)();
//成员函数
typedef void(A::*MemberFuncp)();

NormalFuncp fun1 = NormalFunc;
MemberFuncp fun2 = &A::MemberFunc;
NormalFuncp fun3 = A::StaticFunc;
A a;
fun1();
(a.*fun2)(); //类的成员函数调用规范,使用".*"
fun3();

return 0;
}




根据上述分析,采用STL::List作为容器,泛化无参数无返回值函数的使用场景,采用原型模式设计”无参数无返回值函数注册机“系统,这篇博主的代码质量很高,不重复造轮子摘录如下。

其代码的UML架构如下



delegate.h

#ifndef _DELEGATE_H_
#define _DELEFATE_H_

#include <typeinfo>
#include <list>

using namespace std;

class IDelegate
{
public:
virtual ~IDelegate() { }
virtual bool isType(const std::type_info& _type) = 0;
virtual void invoke() = 0;
virtual bool compare(IDelegate *_delegate) const = 0;
};

class CStaticDelegate : public IDelegate
{

public:
typedef void (*NormalFunPtr)();

CStaticDelegate(NormalFunPtr _func):mFun(_func) {}

virtual bool isType(const std::type_info& _type)
{
return typeid(CStaticDelegate) == _type;
}

virtual void invoke()
{
mFun();
}

virtual bool compare(IDelegate* _delegate) const
{
if ( 0 == _delegate || !_delegate->isType( typeid(CStaticDelegate) ) )
return false;
CStaticDelegate* cast = static_cast<CStaticDelegate*>(_delegate);
return cast->mFun == mFun;
}

private:
NormalFunPtr mFun;
};

/*注册类非静态函数成员的委托,非静态函数成员的函数指针为void (ClassName::*FunName)();
**这里ClassName不确定,所以要么采用宏,要么采用模板类,这里采用模板类
*/
template<class T>
class CMethodDelegate : public IDelegate
{
public:
typedef void (T::*Method)();

CMethodDelegate(T* _object, Method _method): mObject(_object), mMethod(_method) {}

virtual bool isType (const std::type_info& _type)
{
return typeid(CMethodDelegate<T>) == _type;
}

virtual void invoke()
{
(mObject->*mMethod)();
}

virtual bool compare( IDelegate* _delegate) const
{
if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T>)) )
return false;
CMethodDelegate<T>* cast = static_cast<CMethodDelegate<T>*>(_delegate);
return cast->mObject == mObject && cast->mMethod == mMethod;
}

private:
T* mObject;
Method mMethod;
};

inline IDelegate* newDelegate(void (*_func)() )
{
return new CStaticDelegate(_func);
}

template<class T>
inline IDelegate* newDelegate( T* _object, void (T::*_method)() )
{
return new CMethodDelegate<T>(_object, _method);
}

class CMultiDelegate
{
public:
typedef std::list<IDelegate*> ListDelegate;
typedef ListDelegate::iterator ListDelegateIterator;
typedef ListDelegate::const_iterator ConstListDelegateIterator;

CMultiDelegate() {}
~CMultiDelegate() {clear();}

bool empty() const
{
for (ConstListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); iter++)
{
if (*iter) return false;
}
return true;
}

void clear()
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter!=mListDelegates.end(); iter++)
{
if (*iter)
{
delete (*iter);
(*iter) = 0;
}
}
}

/*重载了 +=  表示向这个委托注册一个函数指针,这个方法会自动判重,如果重复了就不会向里面添加。*/
CMultiDelegate& operator+= (IDelegate* _delegate)
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter!= mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
delete _delegate;
return *this;
}
}
mListDelegates.push_back(_delegate);
return *this;
}

/*重载了  -=  表示向这个委托注销一个函数指针,如果这个函数指针不存在就什么也不执行。*/
CMultiDelegate& operator-= (IDelegate* _delegate)
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter!= mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter); //如果是指向同一个IDelegate对象,即*iter=_delegate, 切勿重复删除
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}

/*重载了  ()   表示当作函数调用启动这个委托,内部就是将所有函数指针指向的函数都运行一遍。*/
void operator() ( )
{
ListDelegateIterator iter = mListDelegates.begin();
while (iter != mListDelegates.end())
{
if ( 0 == (*iter))
{
iter = mListDelegates.erase(iter);
}
else
{
(*iter)->invoke();
++iter;
}
}
}

private: //堵死这两种带参构造函数
CMultiDelegate (const CMultiDelegate& _event) { }
CMultiDelegate& operator= (const CMultiDelegate& _event) { }

private:
ListDelegate mListDelegates;
};

#endif


test.cpp

#include "delegate.h"
#include <iostream>
#include <cstdio>

using namespace std;

void NormalFunc()
{
printf("这里是普通函数\n");
}

class A
{
public:
int i;
A(int in):i(in) { }

static void StaticFunc()
{
printf("这里是成员静态函数\n");
}
void MemberFunc()
{
printf("这里是成员非静态函数%d\n", i);
}
};
int main()
{
//普通函数
typedef void(*NormalFuncp)();
//成员函数
typedef void(A::*MemberFuncp)();

NormalFuncp fun1 = NormalFunc;
MemberFuncp fun2 = &A::MemberFunc;
NormalFuncp fun3 = A::StaticFunc;
A a(10);
fun1();
(a.*fun2)();
fun3();

CMultiDelegate onClick;
onClick += newDelegate(NormalFunc);
onClick += newDelegate(&A(100), &A::MemberFunc);

onClick();

return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 事件委托机制