好 我们现在来看怎么用它
我们首先定义一个新型 EventDelegator
用它来作为事件委托者型
然后我们定义一个指向类的成员函数的指针型 ValueChanged
它需要带两个参数
一个参数是 value 一个参数是 tag
value 就是那个值发生变化以后
它的那个新值
tag 就是一个附加的参数
这是一个指向类成员函数的指针
所以我们前面要给它定义好它的类
这个类就是我们的事件委托者类 EventDelegator
接下来是 Trigger 触发者类的定义
在触发者类里 我们只需要定义一个
私有的数据成员 _value
用它来保存这个对象的一个特定的值
一个 _value
这个值如果发生变化
它就会产生一个值发生变化的事件
也就是所谓的值变更事件
所以我们又定义了一个公开的属性 value_changed
当值发生变化的时候 这个 value_changed
这样的一个值发生变化的属性里边
所有的事件响应者都必须做出行动
所以 value_changed 这样的一个属性
我们要使用 Event〈ValueChanged〉
这样的一个事件型对它进行定义
ValueChanged 就是 Event 这个事件类模板的实际参数
我们就用它定义 value_changed
这一个值变更事件的公开属性
为什么要把它定义成公开的呢
理论上来讲 这么定义不是不太好吗
这是一个属性
但实际上是为了方便我们应用
如果要严格地保持它的数据封装与信息隐藏的话
那么事实上我们最好的方案是把它定义成 protected
类的定义是简单的
现在我们来实现
它最重要的一个函数 SetValue()
我们最难的地方来了
trigger 这是一个触发值变更事件的一个对象
当它设定它的 _value 值的时候
它就会产生一个值变更事件
在这个时候
所有响应这个值变更事件的对象都必须做出行动
这个行动什么时候触发呢
当然在这个值被设定的时候触发
所以 SetValue() 就将负责
所有事件响应者的响应行动的那个调用
这是 SetValue() 要做的事情
首先 当这个新的 value 值和原值是相等的时候 我们什么也不做
这个值没有发生变更
所以我们什么都不做
否则的话我们将设定新值
将 _value 赋值为 value
然后我们要得到
所有响应这个值变更事件的事件响应者列表 也就是那个 vector
这个 vector 存在什么地方呢
存在我们的 Event〈ValueChanged〉这个类的
一个对象里 那个对象在哪里
就在我们的 Trigger 类里那个公开的属性 value_changed 那里 响应者呢
就是那个向量里的每一个元素
所以我们通过 this 这个指针
访问 value_changed 这个公开的属性
然后调用 GetResponsors()
得到那个事件响应者向量
我们这里使用了两行代码
是因为我这一行实在是写不下了
那个对象的型是 Event〈ValueChanged〉::EventResponsors
所以我们定义 Event〈ValueChanged〉::EventResponsors 的一个对象 ers
把 ers 赋值为 this->value_changed.GetResponsors()
这样的话
我们就得到了这个事件响应者向量
接下来做什么
在这些事件响应者上调用它们的事件响应行为
所以如果这个向量非空
那么我们就定义一个迭代器 it
这个迭代器的型当然是 Event〈ValueChanged〉::EventIterator
我们定义这样的一个迭代器
迭代 每找到一个事件响应者
我们就调用它的事件响应函数
响应我们这个值变更事件
循环做的就是这个事
问题是怎么调用
这是我们事件机制里最难的一个位置
it 是什么
it 不是那个事件响应者对象
it 相当于指向那个事件响应者的指针
那个事件响应者是什么
是 EventResponsor 对吧
我们在这个向量里存的是 EventResponsor
当然它依然是一个类模板
所以实际上存的是 EventResponsor〈ValueChanged〉
响应这个值变更事件的事件响应者们
存的是这个东西
每个元素都是 EventResponsor〈ValueChanged〉
*it 就指代那个事件响应者
而 it 是指向它的 记住这一点
每一个事件响应者里有两个成员
一个是 actor 一个是 action
一个是指向响应这个行动的那个对象的指针
一个是指向指向响应行动函数的指针的指针 对吧
我们当时实现事件机制的时候
就是按照这个方式实现的
我们怎么访问事件响应者呢
it->actor 得到的就是那个 actor 字段
我们怎么访问它的行动呢
it->action 得到的就是那个 action 字段
it->action 是什么
it->action 是指向指向类的成员函数的指针的指针
我们怎么得到指向类成员函数的指针 引领
所以我要在 it->action 前边
加一个 “*” 引领
得到指向类的成员函数的指针
然后我们就想调用它
这个 *(it->action)
是指向类的成员函数指针没错
那么它到底是在哪一个对象上调用呢
它在 it->actor 那个对象上调用
所以你要想得到 it->actor
这个对象上的成员函数的指针
那么我们必须用 “->*” 这个模式去得到它
所以严格的写法就是 (it->actor)->*(*(it->action))
这才得到指向 actor 这个对象的那个类的成员函数的指针
也就是说 严格讲起来
实际上是在 actor 这个对象上
调用指向它的类的成员函数的指针
我们想做的其实就是这个事
所以你看 这里最复杂的地方
就是 (it->actor)->*(*(it->action))
特别得费劲 用起来很复杂
这里面一堆括号
(it->actor)->*(*(it->action))
一堆括号 太费劲了
其实这些括号都可以不写 为啥呀
因为这几个操作符的优先级很碰巧
优先级最高的是 “->” 符号
其次是 “*” 号 再次是 “->*” 号
所以这里面的括号其实都不需要写
现在我们得到的就是那个 actor 对象的指向类的成员函数的指针
我们要在这个基础之上调用它的成员函数 怎么调用呢
在外面封装一个括号
这样你就可以把它当做一个普通的函数一样用了
然后我们后边传两个参数 value、tag
完成这个调用
最外面这两个括号是不能省略的
里面的那些括号都可以省略
这个就是我们事件机制的调用
非常非常费劲
你看 SetValue() 这个函数
每设定一个新值 它就会遍历我们这个事件响应者的向量
然后一个接着一个地调用里边事件响应者的成员函数
去响应这个事件 做的就是这个事
接下来是 Actor 行动者的定义
行动者要做三件事情
一个 它要负责侦听这个事件
有一个成员函数叫 Listen()
它需要传递一个 trigger
trigger 是指向触发器的一个指针
我们要侦听这个事件
那么这个事件由谁导致的呢
当然是触发器
我们就侦听这个 trigger 对象的事件
就是这个意思
所以我们要在 trigger 上
调用它的 value_changed 这个属性的 Bind() 成员函数
将 Actor 这个行动者绑定到它的那个值变更事件上
所以它需要传两个参数
一个是它自己 this
第二个就是响应这个值变更事件的那个事件响应函数的入口地址
传的就是 &Actor::OnValueChanged
把自己和响应那个事件的自己的那个成员函数绑定到触发器对象上
这样的话就把这个事件响应函数
写到了那个触发器对象的事件响应列表里
这是 Listen()
Unlisten() 就是取消这个侦听动作
我不听了 我不管了 就这个意思
所以它的具体的实现和 Listen() 是一样的
也只有一行代码
只不过是把那个 Bind() 成员函数的调用
换成了 Unbind() 成员函数的调用
第三个就是值变更事件的响应函数 OnValueChanged()
它的实现就比较简单了
我们只需要简单地输出一行信息就完了
它那个附加参数 viod * tag 传的是什么呢
传的就是额外的字符串信息
所以我们在这里边要把它转换成 char *
把它输出 很简单就完成了
这是 Actor 行动者
我们看主函数 在主函数内
我定义一个字符串 “Now the value is”
然后定义一个触发器对象 t
定义两个行动者对象 a1 和 a2
然后在 a1 上调用 Listen()
在 a2 上调用 Listen()
传的参数都是 Trigger 那个对象 t 的地址
也就是说 现在我们有两个行动者将负责响应
那个触发器对象 t 的值变更事件
那么我们现在就为 t 这个对象
设定一个新值 10 我们传递 s 字符串
在这里要把 s 字符串首先做一次常量转型
把它的 const 给去掉
然后再做一次复诠转型
把它转成哑型指针作为附加参数传进去
虽然连续两次转型看上去很讨厌
也很麻烦
但是这是最科学最安全的方法
接下来我们 a2.Unlisten()
取消 a2 这个对象的侦听动作
这样的话 a2 这个对象
将从 t 这个对象的事件响应者列表中被删除
我们重新设定一次新值
然后你就能够看到
当第一次设定新值的时候
有两个对象去侦听它的值变更事件
而当取消 a2 这个对象的侦听之后
那么再次设定新值
将只有 a1 一个对象响应这个值变更事件
这就是我们事件机制的全部实现
同学们一定要掌握它
尤其是指向类的成员函数的指针的用法
在一些复杂的程序架构里边
没有它是很难实现的