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,等就是事件,委 ...
随机推荐
- Openstack+Kubernetes+Docker微服务实践之路--RPC
重点来了,本文全面阐述一下我们的RPC是怎么实现并如何使用的,跟Kubernetes和Openstack怎么结合. 在选型一文中说到我们选定的RPC框架是Apache Thrift,它的用法是在Ma ...
- Mysql 5.7.12 配置
打算用express+mysql写一个博客.本来在公司电脑已经配置好了的,但是为了方便在家里也能修改,所以在自己的电脑里也安装好环境. 公司电脑是win7系统32位的,安装的是5.5的mysql,用的 ...
- [地图SkyLine二次开发]框架(5)完结篇
上节讲到,将菜单悬浮到地图上面,而且任何操作都不会让地图把菜单盖住. 这节带大家,具体开发一个简单的功能,来了进一步了解,这个框架. 1.想菜单中添加按钮 -上节定义的mainLayout.js文件里 ...
- python成长之路【第八篇】:异常处理
一.异常基础 在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!! 语法: try: pass except Exc ...
- RabbitMQ之入门
生成者: #coding:utf-8 import sys import pika credentials=pika.PlainCredentials("guest"," ...
- centos6.8服务器部署svn
1. 安装svn yum list svn* yum install subversion 2. 测试svn安装是否成功 svnserve –version 3. 创建代码仓库 mkdir /usr/ ...
- Python之路 day3 函数定义 *args及**kwargs
#!/usr/bin/env python # -*- coding:utf-8 -*- #Author:ersa import time # def logger(): # time_format ...
- table清除样式大全
table{width:100%;text-align:center;border-collapse:collapse;border-spacing:1;border-spacing:0; }tabl ...
- 基础算法之冒泡排序Bubble Sort
原理 将相邻的数据两两进行比较,按照从小到大或者从大到小的顺序进行位置交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后从头开始再次进行两两比较交换,直到倒数第二位时结束.按照此规则,若干 ...
- android studio gradle结构项目引入本地代码
1.首先需要用eclipse打开目标项目,file export,选择gradle file. 2.拷贝文件到as项目的根目录[可选] 3.找到as项目的根目录下 .idea目录,下面有个module ...