事件基本概念

  • 操作系统或应用程序内部发生某件事,程序的某个组件需要响应该事件,并进行特定处理

面向对象架构中,事件响应函数最可能为成员函数

  • 问题:指向类成员函数的指针不能转换为哑型指针void *,也不能随意转换为指向另一个类的成员函数的指针
  • 解决方案:使用指向指向类成员函数的指针的指针

实现策略:事件委托模型

  • Event类模板:管理事件响应者对象,实现事件多播
  • EventResponsor类模板:响应者对象与响应者行为配对
  • Empty类:委托模型和指针转换
#include <iostream>
#include <vector>
using namespace std;
//空类,用于指代响应者对象
class Empty{};
//事件响应者类模板,保存特定事件的响应者与响应行为
template<typename EventAction> class EventResponsor
{
public:
EventResponsor():actor(NULL),action(NULL) {}
EventResponsor(Empty *actor,EventAction *action):actor(actor),action(action){}
friend bool operator==(const EventResponsor &lhs, const EventResponsor &rhs)
{
return lhs.actor == rhs.actor && *lhs.action == *rhs.action;
}
public://公开的数据成员,以方便使用者
Empty *actor;
EventAction *action;
};//template<typename EventAction> class EventResponsor
//事件类模板,用于管理特定事件的所有响应者
template<typename EventAction> class Event
{
public:
typedef vector<EventResponsor<EventAction> >EventResponsors;
typeder typename vector<EventResponsor<EventAction> >::iterator EventIterator;
public:
virtual ~Event()
{
for (EventIterator it = this->_ers.begin(); it != this->_ers.end(); ++it)
{
delete it->action, it->action = NULL;
}
}
EventResponsors & GetResponsors() { return this->_ers; } //事件绑定,将实际响应者和响应行为挂接到事件响应者对象上
template<typename Responsor, typename Action>
void Bind(Responsor *actor, Action action)
{
Action *act = new Action(action);
EventResponsor<EventAction> er((Empty*)actor, (EventAction*)act);
bool unbound = true;
for (EventIterator it = this->_ers.begin(); it != this->_ers.end(); ++it)
{
if (*it == er)//发现重复的事件响应者,说明已绑定
{
unbound = false;
break;
}
}
if (unbound)
{
this->_ers.push_back(er);
}
else
{
delete er.action, er.action = NULL;
}
}
//解除事件绑定,删除事件响应者对象
template<typename Responsor,typename Action>
void Unbind(Responsor *actor, Action action)
{
Action *act = new Action(action);
EventResponsor<EventAction> er((Empty*)actor, (EventAction *)act);
for (EventIterator it = this->_ers.begin(); it != this->_ers.end(); ++it)
{
if (*it == er)//找到待删除的事件响应者对象
{
delete it->action, this->_ers.erase(it); break;
}
}
delete er.action, er.action = NULL;
}
private:
EventResponsor _ers;
};//template<typename EventAction> class Event
//定义事件委托模型,指向类成员函数的指针
typedef Empty EventDelegator;
typedef void(EventDelegator::*ValueChanged)(int value, void *tag);
//触发者
class Trigger
{
public:
Trigger() :_value() {}
void SetValue(int value, void *tag);
int GetValue() { return _value; }
public:
//值变化事件,公开属性,方便在类外设定
Event<ValueChanged> value_changed;
private:
int _value;
};
//设定值,遍历特定事件的响应对象列表,逐一触发值变更事件
void Trigger::SetValue(int value, void *tag)
{
if (_value == value)
{
return;
}
_value = value;
Event<ValueChanged>::EventResponsors ers;
ers = this->value_changed.GetResponsors();
if (!ers.empty())
{
Event<ValueChanged>::EventIterator it;
for ( it = ers.begin(); it != ers.end(); ++it)
{
((it->actor)->*(*(it->action)))(value, tag);//响应事件
}
}
}
//行动者
class Actor
{
public:
//侦听事件,绑定本对象的事件响应函数到侦听的事件
void Listen(Trigger *trigger)
{
trigger->value_changed.Bind(this, &Actor::OnValueChanged);
}
//停止侦听,从侦听的事件中取消本对象的事件响应活动
void Unlisten(Trigger *trigger)
{
trigger->value_changed.Unbind(this, &Actor::OnValueChanged);
}
//值变更事件的响应函数
void OnValueChanged(int value, viod *tag)
{
cout << reinterpret_cast<char *>(tag) << value << "." << endl;
}
}; int main()
{
const char *s = "Now the value is";
Trigger t;
Actor a1, a2; a1.Listen(&t);
a2.Listen(&t); cout << "Listening..." << endl;
t.SetValue(, reinterpret_cast<void *>(const_cast<char *>(s))); a2.Unlisten(&t);
cout << "Listening again..." << endl;
t.SetValue(, reinterpret_cast<void *>(const_cast<char *>(s)));
return ;
}

C++学习笔记38:事件机制的更多相关文章

  1. NodeJS学习笔记 (21)事件机制-events(ok)

    模块概览 events模块是node的核心模块之一,几乎所有常用的node模块都继承了events模块,比如http.fs等. 模块本身非常简单,API虽然也不少,但常用的就那么几个,这里举几个简单例 ...

  2. java学习笔记09--反射机制

    java学习笔记09--反射机制 什么是反射: 反射是java语言的一个特性,它允许程序在运行时来进行自我检查并且对内部的成员进行操作.例如它允许一个java的类获取他所有的成员变量和方法并且显示出来 ...

  3. Storm学习笔记 - 消息容错机制

    Storm学习笔记 - 消息容错机制 文章来自「随笔」 http://jsynk.cn/blog/articles/153.html 1. Storm消息容错机制概念 一个提供了可靠的处理机制的spo ...

  4. 学习笔记---Javascript事件Event、IE浏览器下的拖拽效果

    学习笔记---Javascript事件Event.IE浏览器下的拖拽效果     1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcE ...

  5. Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle<T>

    Caliburn.Micro学习笔记目录 今天 说一下Caliburn.Micro的IEventAggregator和IHandle<T>分成两篇去讲这一篇写一个简单的例子 看一它的的实现 ...

  6. iOS学习笔记--触摸事件

    最近空闲时间在学习iOS相关知识,几周没有更新文章了,今天总结下这些天的学习内容,也整理下iOS的学习笔记,以便以后查阅翻看- iOS中的事件可以分为3大类型: 触摸事件 加速计事件 远程控制事件 响 ...

  7. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

  8. [JS学习笔记]Javascript事件阶段:捕获、目标、冒泡

    当你在浏览器上点击一个按钮时,点击的事件不仅仅发生在按钮上,同时点击的还有这个按钮的容器元素,甚至也点击了整个页面. 事件流 事件流描述了从页面接收事件的顺序,但在浏览器发展到第四代时,浏览器开发团队 ...

  9. vue学习笔记(四)事件处理器

    前言 在上一章vue学习笔记(三)class和style绑定的内容中,我们学习了如何在vue中绑定class和style,介绍了常用的绑定方法,class的数组绑定和对象绑定以及style的数组绑定和 ...

  10. JavaScript高级程序设计学习笔记之事件

    1.事件流 事件流描述的是从页面中接收事件的顺序. 事件冒泡 IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播 ...

随机推荐

  1. gitlab代码仓库迁移

    有的时候我们需要对gitlab上的代码进行迁移,希望在迁移后能保持原有的branch.tag.commit记录等.可以使用以下方式: 1.clone代码到本地. 2.修改remote仓库的地址,添加新 ...

  2. 转:MySQL如何修改密码

    转:https://www.cnblogs.com/yang82/p/7794712.html. 第一种方式: 最简单的方法就是借助第三方工具Navicat for MySQL来修改,方法如下: 1. ...

  3. Laravel Eloquent 数据查询结果中日期的格式化

    两种情况: 使用 Model 的查询 例如: $item = App\Models\Apple::first(); $date = $item->created_at->format('Y ...

  4. 步步为营-71-asp.net的简单练习(图片处理)

    1 原有图片添加水印 1.1 封装一个类,用于获取文件路径 using System; using System.Collections.Generic; using System.IO; using ...

  5. 演示Thread.sleep(100)和Thread.currentThread().isInterrupted()+@Deprecated:将方法标注为废弃的方法

    package charpter08; public class TestInterrupt01 { public static void main(String[] args) { Processo ...

  6. Mac下Eclipse读取不到环境变量

    问题: 用Eclipse时候读取不到 ~/.bash_profile 下定义的环境变量,确切的说,是GUI应用读取不到终端的(如eclipse) 解决: 1,下载启动代理器: curl https:/ ...

  7. for循环输出数组中的分数

    示例 var scores = [24, 32, 17]; // A数组 var arrayLength = scores.length;// 数组的长度 //当i<arrayLength时,可 ...

  8. django的FormView中,自定义初始化表单数据的曲折方法

    这个技巧,主要是用于表单初始化及回显. 也就是说,如果用户的数据库里有数据,则要将相应的数据显示在表单里, 如果用户的数据库里没有数据,才会生成一个空白的表单给用户, 这样才显得专业塞! 而我面对的尴 ...

  9. mybatis中的查询语句in用法的相关问题

    在开发的时候,mybatisl中使用in的时候会遇到一些问题,如果我们传的参数是String类型,以“,”来进行隔开的,例如:参数是0,1,2字符串,mybatis中的语句如下 <select ...

  10. [转]Mysql 存储过程和函数区别

    http://blog.csdn.net/wangsifu2009/article/details/6725213 存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用 ...