C++模拟C#事件委托机制(二)
原文 来自于http://www.cnblogs.com/netssfy/archive/2010/02/02/1662056.html
为了解决非法地址访问的冲突,首先需要知道发生该错误的原因是什么
事件与委托的关系是1对多的,事件与委托对象实例的关系是多对多的,所以使用CListenerAgent将这种多对多的关系拆开。而每个CListenerAgent是事件与委托对象实例的一一对应
对于事件来说当事件本身销毁时,所有订阅的委托都应该销毁,否则存在内存泄漏。
对于委托对象实例来说,当本身销毁时,所有已经订阅的委托(可以订阅多个不同的事件)需要告知对应的事件,在事件发生时不用再通知它了,因为他已经不存在了。
所以实现这个的核心还是CListenerAgent,让CListenerAgent管理事件实例和委托对象实例,当他们销毁时可以及时的通过CListenerAgent去通知另一端。
同时,因为每个对象可以订阅多个事件,所以也需要一个类型帮助对象去管理CListenerAgent。
CListener孕育而生,所有想要订阅到CEvent类上的类型,都必须继承这个CListener,因为他会帮你去管理,所有的CListenerAgent。这样用户可以专注于自己的逻辑实现。
前面说过了一个对象可以订阅到多个事件上,那么CListenerAgent的第2个模板参数肯定会不同,那怎么让CListener管理各种不同的CListenerAgent呢?
答案还是接口。想想我们在CListener里管理CListenerAgent的作用是什么?只是为了CListener的派生类在销毁时能通过CListenerAgent去通知CEvent反订阅这个委托。
对,这里没有任何的其他类型相关的要求。所以设计一个接口
class IDispose
{
public:
/*Dispose, free memory*/
virtual void Dispose() = 0;
};
CListener 去会维护一个这个接口的LIST,而CListenerAgent则会实现这个接口。
Dispose的工作就是删除自己,并且将自己从CEvent和CListener的list中移除。
同样,CEvent也是在析构时依次调用Dispose去完成清理工作。
实现如下:
代码如下:
/*
Description:This library allow programmer use event feature like C# with C++ language
Author:SeigfriedY
Usage:
CEvent<CEventArgs> event;
CEventArgs args;
Observer* pO = new Observ er(); //Observer has a function : void Handler(CEventArgs)
CListenerAgent<Observer, CEventArgs>* agent = new CListenerAgent<Observer, CEventArgs>(pO, &Observer::Handler);
event += agent;
event(args);
event -= agent;
ps:CEventArgs is a argument type which define the type of event, it can be any type;
Note:
1.If the type would be a listener to subscribe some events on other type, it must inherit from CListener.
2.The return type of handler function must be void.
3.CListenerAgent should be create on heap(use new operator to create the instance)
4.Programmer can't and never to delete CListenerAgent's instance, the life cycle is depend on CEvent and the relative observer's instance
Design:
As one observer can subscribe on many different events, and one event can be subscribe from many different observer type, they are "N to N"
relationship.So need a class to decompose the one relationship into 2 like "1 to N" and "N to 1", this class is named CListenerAgent.
One observer can have multi CListenerAgent and one CEvent also can manage multi CListnerAgent.
CListenerAgent's responsibility is to build a "1 to 1" bridge between observer and CEvent.
Use interface to avoid the type-related, only the CListenerAgent need know the observer's concrete type in detail.
ClistenerAgent decrease the coupling between subject and observer.
User interface in internal to avoid type-related, So CEvent<ArgsT> only need know IListenerAgent<ArgsT> instead of CListenerAgent<ObserverT, ArgsT>.
Since CEvent only need to fire the event which contain the ArgsT, it needn't know the type of observer.This relationship is in CListenerAgent.
*/
#pragma once
#include <list>
#include <iostream>
using namespace std;
/*Declaration*/
template<typename ArgsT>
class CEvent;
/*A sample arguments class*/
class CEventArgs
{
private:
void* _args;
public:
CEventArgs(void* args)
{
_args = args;
};
void* GetArgs()
{
return _args;
}
};
/*
The responsibility of ICallback interface is to Dispose
*/
class IDispose
{
public:
/*Dispose, free memory*/
virtual void Dispose() = 0;
};
/*The responsibility of IListener interface is to add ICallback into
CListener which implement this interface.*/
class IListener
{
public:
/*Add ICallback*/
virtual void AddListenerCallback(IDispose* agent) = 0;
/*When CListener is disposed, it will notify this event to all ICallback which has been added*/
virtual void NotifyDispose() = 0;
/*Remove the IDispose from list*/
virtual void Remove(IDispose* agent) = 0;
};
/*
The responsibility of IListenerAgent is:
1.When event has been fired, notify the listener to execute the event handler which has been subscribed
2.When CListenerAgent is being subscribed on event, set the event instance
*/
template<typename ArgsT>
class IListenerAgent : virtual public IDispose
{
public:
/*Fire the event*/
virtual void Fire(ArgsT) = 0;
/*Set the event instance*/
virtual void SetEventCallback(CEvent<ArgsT>* pEvent) = 0;
};
/*
This class help Observer class to manage the CListenerAgent class which subscribe to CEvent
Since CListener is inherited by user's observer class, so when initialize the CListenerAgent,
CListenerAgent will call IListener->AddListenerCallback(this), to register itself to CListener.
When CListener's destructor is called, it will notify all CListenerAgent that it managed to
dispose themselves
Note: User's observer class must inherit from this class if it want to be listenable.
*/
class CListener : public IListener
{
private:
list<IDispose *> _agents;
public:
CListener()
{
}
virtual ~CListener()
{
NotifyDispose();
};
private:
/*IListener*/
/*Add ICallback(CListenerAgent) and manage them*/
virtual void AddListenerCallback(IDispose* agent)
{
if (agent != NULL)
{
_agents.push_back(agent);
}
}
/*When destructor, notify all ICallback(CListenerAgent) to dispose themselves*/
virtual void NotifyDispose()
{
list<IDispose*>::iterator it, end;
IDispose* temp;
it = _agents.begin();
end = _agents.end();
for (it ; it != end ; )
{
temp = *it;
it++;
temp->Dispose();
}
}
/*Remove the agent from list*/
virtual void Remove(IDispose* agent)
{
_agents.remove(agent);
}
};
/*
The core class.This class set up a link between CEvent and CListener.
This class implement IListenerAgent<ArgsT> and ICallback
*/
template<typename ObserverT, typename ArgsT>
class CListenerAgent : virtual public IListenerAgent<ArgsT>
{
private:
/*The type of observer's handler which is to handle the event when event arise*/
typedef void (ObserverT::*ObserverDelegate)(ArgsT);
/*Two function pointers*/
ObserverDelegate _pObserverDelegate;
/*CListenerAgent keep these pointer to keep the contact between CEvent and observer*/
/*These two pointers point to the same memory area, of course, their value may not be equal.*/
ObserverT* _pObserver;
IListener* _pListener;
/*This point to the event instance which subscribe on*/
CEvent<ArgsT>* _pEvent;
public:
/*Default Constructor*/
CListenerAgent()
{
_pEvent = NULL;
_pObserverDelegate = NULL;
_pObserver = NULL;
}
/*Constructor*/
CListenerAgent(ObserverT* observer, ObserverDelegate d)
{
_pObserver = observer;
_pListener = (IListener*)_pObserver;
_pObserverDelegate = d;
_pEvent = NULL;
/*Register itself to CListener,*/
_pListener->AddListenerCallback((IDispose*)this);
}
private:
/*Destructor.Notify the CEvent to remove itself from the list*/
virtual ~CListenerAgent()
{
/*remove the agent from event*/
if (_pEvent != NULL)
{
(*_pEvent) -= ((IListenerAgent<ArgsT>*)this);
}
/*remove the agent from listener*/
if (_pListener != NULL)
{
_pListener->Remove((IDispose*)this);
}
}
/*IListenerAgent*/
/*Notify the observer to handle the event*/
virtual void Fire(ArgsT args)
{
if (_pObserver != NULL)
{
(_pObserver->*_pObserverDelegate)(args);
}
}
/*Keep the CEvent instance*/
void SetEventCallback(CEvent<ArgsT>* pEvent)
{
_pEvent = pEvent;
}
/*IDispose*/
virtual void Dispose()
{
delete this;
}
};
/*
CEvent class has 3 responsibility:
1.Provide the friendly operator like C#
2.When event arise, notify all listeners
3.When disposed, notify all CListenerAgent
*/
template<typename ArgsT>
class CEvent
{
private:
/*Class member handler type, use IListenerAgent to complete the work*/
typedef IListenerAgent<ArgsT>* Listener;
/*Global or static handler type*/
typedef void (*StaticListener)(ArgsT);
/*Hold all class member handlers in a list*/
list<Listener> _listeners;
/*Hold all global or static handlers in a list*/
list<StaticListener> _staticListeners;
public:
/*Default constructor*/
CEvent()
{
}
/*Default destructor*/
virtual ~CEvent()
{
list<Listener>::iterator it, end;
Listener temp;
it = _listeners.begin();
end = _listeners.end();
for (it ; it != end ;)
{
temp = *it;
it++;
((IDispose*)temp)->Dispose();
}
_listeners.clear();
_staticListeners.clear();
}
/*Subscribe class member listener*/
void operator+=(const Listener listener)
{
Subscribe(listener);
};
/*Subscribe global or static listener*/
void operator+=(const StaticListener listener)
{
Subscribe(listener);
}
/*Unsubscribe class member listener*/
void operator-=(const Listener listener)
{
Unsubscribe(listener);
};
/*Unsubscribe class member listener*/
void operator-=(const StaticListener listener)
{
Unsubscribe(listener);
}
/*Fire the event*/
void operator()(ArgsT args)
{
FireEvent(args);
}
private:
/*Unsubscribe the listener if be found in list*/
void Unsubscribe(Listener listener)
{
_listeners.remove(listener);
}
/*Unsubscribe the listener if be found in list*/
void Unsubscribe(StaticListener listener)
{
_staticListeners.remove(listener);
}
/*Fire the event*/
void FireEvent(ArgsT args)
{
list<Listener>::iterator it, end;
it = _listeners.begin();
end = _listeners.end();
for (it ; it != end ; it++)
{
((Listener)*it)->Fire(args);
}
list<StaticListener>::iterator it2, end2;
it2 = _staticListeners.begin();
end2 = _staticListeners.end();
for (it2 ; it2 != end2 ; it2++)
{
(*it2)(args);
}
}
/*Subscribe the listener if not be found in list*/
void Subscribe(Listener listener)
{
if (listener != NULL)
{
list<Listener>::iterator it, end;
it = _listeners.begin();
end = _listeners.end();
for (it ; it != end ; it++)
{
if ((*it) == listener)
{
return;
}
}
listener->SetEventCallback(this);
_listeners.push_back(listener);
}
};
/*Subscribe the listener if not be found in list*/
void Subscribe(StaticListener listener)
{
if (listener != NULL)
{
list<StaticListener>::iterator it, end;
it = _staticListeners.begin();
end = _staticListeners.end();
for (it ; it != end ; it++)
{
if ((*it) == listener)
{
return;
}
}
_staticListeners.push_back(listener);
}
};
};
C++模拟C#事件委托机制(二)的更多相关文章
- C++模拟C#事件委托机制(一)
原文来自于http://www.cnblogs.com/netssfy/articles/1652671.html 写了一段时间的C#代码后确实发现C#的事件委托非常好用.于是便想是否在C++中也能如 ...
- Javascript事件模型系列(二)事件的捕获-冒泡机制及事件委托机制
一.事件的捕获与冒泡 由W3C规定的DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监 ...
- 【Unity3D技巧】在Unity中使用事件/委托机制(event/delegate)进行GameObject之间的通信 (二) : 引入中间层NotificationCenter
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 一对多的观察者模式机制有什么缺点? 想要查看 ...
- JavaScript 之默认行为 DOM2级,事件委托机制
1. 事件默认行为及阻止方式 1.1 浏览器的默认行为 JavaScript事件本身所具有的属性,例如a标签的跳转,Submit按钮的提交,右键菜单,文本框的输入等. 1.2 ...
- Android Touch事件传递机制 二:单纯的(伪生命周期)
转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...
- javascript事件委托机制详解
以个人前端工作面试经历来看,javascript事件委托是问的最多的一类题目之一,熟悉事件委托能够了解你对于javascript的掌握程度. 面试官可能问一下问题,现在有5个li待办事件,需要实现当点 ...
- 事件/委托机制(event/delegate)(Unity3D开发之十七)
猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/46539433 ...
- Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点
转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...
- JS与Jquery的事件委托机制
传送:http://www.ituring.com.cn/article/467 概念: 什么是事件委托:通俗的讲,事件就是onclick,onmouseover,onmouseout,等就是事件,委 ...
随机推荐
- avalon学习笔记
背景: 刚刚到一家新的公司,我新接手的项目采用的mvvm框架是avalon,当然我以前没有学过angular,但是用过react,所以对于这类框架应该不算陌生.但毕竟是一个新的框架,所以还是先学起来, ...
- redis安装
参考redis.io 安装步骤 下载,编译reids $ wget http://download.redis.io/releases/redis-3.0.6.tar.gz $ tar xzf red ...
- 卷土重来之staticHtml基础使用教程
前段时间发布了一个asp.net生存html缓存的东西,老实说坑了蛮多的人,bug比较多, 经过这段时间的测试与改进,应该到了可以使用的地步了, 欢迎大家测试与使用,下面我介绍使用教程,对了,这里感谢 ...
- 跨站请求伪造 CSRF / XSRF<二:应用>
防御的方法主要有两种<java示例> 1.检查Referer字段 HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址.在处理敏感数据请求时,通常来说,Referer字 ...
- freeMarker生成静态页面
项目结构图 footer.ftl des==>${f.des}<br/> <a href="http://www.baidu.com"> 百度 < ...
- myeclipse 没有任何问题,可偏偏还报这错。
Windows–>Preferences–>Java–>Compiler–>Building–>Output folder–> ”Rebuild class fil ...
- PHP data
- zabbix3.0.4 部署之九 (zabbix3.0.4 Linux Agent安装)
1.从官网现在源码镜像 http://nchc.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/3.2.1/zabbix-3. ...
- 深入理解javascript系列,读书笔记
深入理解JavaScript系列(2):揭秘命名函数表达式 1.讲了函数声明和函数表达式的区别,包括一些在函数提升上的区别 2.如果给函数表达式的函数也取名,会在调试的时候受益 3.不要在block( ...
- 移动端重构实战系列2——line list
这个line list的名字是我自己起的(大概的意思是单行列表),要实现的东西为sheral的line list,对应的scss组件为_line-list.scss,下图为line-list的一个缩影 ...