当前课程知识点:基于Linux的C++ >  第十一讲 泛型编程 >  11.18 泛型编程实践(十) >  LinuxCPP1118

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1118在线视频

LinuxCPP1118

下一节:LinuxCPP1119

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP1118课程教案、知识点、字幕

好 我们现在来看怎么用它

我们首先定义一个新型 EventDelegator

用它来作为事件委托者型

然后我们定义一个指向类的成员函数的指针型 ValueChanged

然后我们定义一个指向类的成员函数的指针型 ValueChanged

它需要带两个参数

一个参数是 value 一个参数是 tag

value 就是那个值发生变化以后

它的那个新值

tag 就是一个附加的参数

这是一个指向类成员函数的指针

所以我们前面要给它定义好它的类

这个类就是我们的事件委托者类 EventDelegator

这个类就是我们的事件委托者类 EventDelegator

接下来是 Trigger 触发者类的定义

在触发者类里 我们只需要定义一个

私有的数据成员 _value

用它来保存这个对象的一个特定的值

一个 _value

这个值如果发生变化

它就会产生一个值发生变化的事件

也就是所谓的值变更事件

所以我们又定义了一个公开的属性 value_changed

所以我们又定义了一个公开的属性 value_changed

当值发生变化的时候 这个 value_changed

这样的一个值发生变化的属性里边

所有的事件响应者都必须做出行动

所以 value_changed 这样的一个属性

我们要使用 Event〈ValueChanged〉

这样的一个事件型对它进行定义

ValueChanged 就是 Event 这个事件类模板的实际参数

ValueChanged 就是 Event 这个事件类模板的实际参数

我们就用它定义 value_changed

这一个值变更事件的公开属性

为什么要把它定义成公开的呢

理论上来讲 这么定义不是不太好吗

这是一个属性

但实际上是为了方便我们应用

如果要严格地保持它的数据封装与信息隐藏的话

如果要严格地保持它的数据封装与信息隐藏的话

那么事实上我们最好的方案是把它定义成 protected

那么事实上我们最好的方案是把它定义成 protected

类的定义是简单的

现在我们来实现

它最重要的一个函数 SetValue()

我们最难的地方来了

trigger 这是一个触发值变更事件的一个对象

当它设定它的 _value 值的时候

它就会产生一个值变更事件

在这个时候

所有响应这个值变更事件的对象都必须做出行动

所有响应这个值变更事件的对象都必须做出行动

这个行动什么时候触发呢

当然在这个值被设定的时候触发

所以 SetValue() 就将负责

所有事件响应者的响应行动的那个调用

这是 SetValue() 要做的事情

首先 当这个新的 value 值和原值是相等的时候 我们什么也不做

首先 当这个新的 value 值和原值是相等的时候 我们什么也不做

这个值没有发生变更

所以我们什么都不做

否则的话我们将设定新值

将 _value 赋值为 value

然后我们要得到

所有响应这个值变更事件的事件响应者列表 也就是那个 vector

所有响应这个值变更事件的事件响应者列表 也就是那个 vector

这个 vector 存在什么地方呢

存在我们的 Event〈ValueChanged〉这个类的

一个对象里 那个对象在哪里

就在我们的 Trigger 类里那个公开的属性 value_changed 那里 响应者呢

就在我们的 Trigger 类里那个公开的属性 value_changed 那里 响应者呢

就是那个向量里的每一个元素

所以我们通过 this 这个指针

访问 value_changed 这个公开的属性

然后调用 GetResponsors()

得到那个事件响应者向量

我们这里使用了两行代码

是因为我这一行实在是写不下了

那个对象的型是 Event〈ValueChanged〉::EventResponsors

那个对象的型是 Event〈ValueChanged〉::EventResponsors

所以我们定义 Event〈ValueChanged〉::EventResponsors 的一个对象 ers

所以我们定义 Event〈ValueChanged〉::EventResponsors 的一个对象 ers

所以我们定义 Event〈ValueChanged〉::EventResponsors 的一个对象 ers

把 ers 赋值为 this->value_changed.GetResponsors()

把 ers 赋值为 this->value_changed.GetResponsors()

这样的话

我们就得到了这个事件响应者向量

接下来做什么

在这些事件响应者上调用它们的事件响应行为

在这些事件响应者上调用它们的事件响应行为

所以如果这个向量非空

那么我们就定义一个迭代器 it

这个迭代器的型当然是 Event〈ValueChanged〉::EventIterator

这个迭代器的型当然是 Event〈ValueChanged〉::EventIterator

我们定义这样的一个迭代器

迭代 每找到一个事件响应者

我们就调用它的事件响应函数

响应我们这个值变更事件

循环做的就是这个事

问题是怎么调用

这是我们事件机制里最难的一个位置

it 是什么

it 不是那个事件响应者对象

it 相当于指向那个事件响应者的指针

那个事件响应者是什么

是 EventResponsor 对吧

我们在这个向量里存的是 EventResponsor

当然它依然是一个类模板

所以实际上存的是 EventResponsor〈ValueChanged〉

所以实际上存的是 EventResponsor〈ValueChanged〉

响应这个值变更事件的事件响应者们

存的是这个东西

每个元素都是 EventResponsor〈ValueChanged〉

*it 就指代那个事件响应者

而 it 是指向它的 记住这一点

每一个事件响应者里有两个成员

一个是 actor 一个是 action

一个是指向响应这个行动的那个对象的指针

一个是指向响应这个行动的那个对象的指针

一个是指向指向响应行动函数的指针的指针 对吧

一个是指向指向响应行动函数的指针的指针 对吧

我们当时实现事件机制的时候

就是按照这个方式实现的

我们怎么访问事件响应者呢

it->actor 得到的就是那个 actor 字段

我们怎么访问它的行动呢

it->action 得到的就是那个 action 字段

it->action 是什么

it->action 是指向指向类的成员函数的指针的指针

it->action 是指向指向类的成员函数的指针的指针

我们怎么得到指向类成员函数的指针 引领

我们怎么得到指向类成员函数的指针 引领

所以我要在 it->action 前边

加一个 “*” 引领

得到指向类的成员函数的指针

然后我们就想调用它

这个 *(it->action)

是指向类的成员函数指针没错

那么它到底是在哪一个对象上调用呢

它在 it->actor 那个对象上调用

所以你要想得到 it->actor

这个对象上的成员函数的指针

那么我们必须用 “->*” 这个模式去得到它

那么我们必须用 “->*” 这个模式去得到它

所以严格的写法就是 (it->actor)->*(*(it->action))

所以严格的写法就是 (it->actor)->*(*(it->action))

这才得到指向 actor 这个对象的那个类的成员函数的指针

这才得到指向 actor 这个对象的那个类的成员函数的指针

也就是说 严格讲起来

实际上是在 actor 这个对象上

调用指向它的类的成员函数的指针

我们想做的其实就是这个事

所以你看 这里最复杂的地方

就是 (it->actor)->*(*(it->action))

特别得费劲 用起来很复杂

这里面一堆括号

(it->actor)->*(*(it->action))

一堆括号 太费劲了

其实这些括号都可以不写 为啥呀

因为这几个操作符的优先级很碰巧

优先级最高的是 “->” 符号

其次是 “*” 号 再次是 “->*” 号

所以这里面的括号其实都不需要写

现在我们得到的就是那个 actor 对象的指向类的成员函数的指针

现在我们得到的就是那个 actor 对象的指向类的成员函数的指针

我们要在这个基础之上调用它的成员函数 怎么调用呢

我们要在这个基础之上调用它的成员函数 怎么调用呢

在外面封装一个括号

这样你就可以把它当做一个普通的函数一样用了

这样你就可以把它当做一个普通的函数一样用了

然后我们后边传两个参数 value、tag

完成这个调用

最外面这两个括号是不能省略的

里面的那些括号都可以省略

这个就是我们事件机制的调用

非常非常费劲

你看 SetValue() 这个函数

每设定一个新值 它就会遍历我们这个事件响应者的向量

每设定一个新值 它就会遍历我们这个事件响应者的向量

然后一个接着一个地调用里边事件响应者的成员函数

然后一个接着一个地调用里边事件响应者的成员函数

去响应这个事件 做的就是这个事

接下来是 Actor 行动者的定义

行动者要做三件事情

一个 它要负责侦听这个事件

有一个成员函数叫 Listen()

它需要传递一个 trigger

trigger 是指向触发器的一个指针

我们要侦听这个事件

那么这个事件由谁导致的呢

当然是触发器

我们就侦听这个 trigger 对象的事件

就是这个意思

所以我们要在 trigger 上

调用它的 value_changed 这个属性的 Bind() 成员函数

调用它的 value_changed 这个属性的 Bind() 成员函数

将 Actor 这个行动者绑定到它的那个值变更事件上

将 Actor 这个行动者绑定到它的那个值变更事件上

所以它需要传两个参数

一个是它自己 this

第二个就是响应这个值变更事件的那个事件响应函数的入口地址

第二个就是响应这个值变更事件的那个事件响应函数的入口地址

传的就是 &Actor::OnValueChanged

把自己和响应那个事件的自己的那个成员函数绑定到触发器对象上

把自己和响应那个事件的自己的那个成员函数绑定到触发器对象上

这样的话就把这个事件响应函数

写到了那个触发器对象的事件响应列表里

这是 Listen()

Unlisten() 就是取消这个侦听动作

我不听了 我不管了 就这个意思

所以它的具体的实现和 Listen() 是一样的

也只有一行代码

只不过是把那个 Bind() 成员函数的调用

换成了 Unbind() 成员函数的调用

第三个就是值变更事件的响应函数 OnValueChanged()

第三个就是值变更事件的响应函数 OnValueChanged()

它的实现就比较简单了

我们只需要简单地输出一行信息就完了

它那个附加参数 viod * tag 传的是什么呢

传的就是额外的字符串信息

所以我们在这里边要把它转换成 char *

把它输出 很简单就完成了

这是 Actor 行动者

我们看主函数 在主函数内

我定义一个字符串 “Now the value is”

然后定义一个触发器对象 t

定义两个行动者对象 a1 和 a2

然后在 a1 上调用 Listen()

在 a2 上调用 Listen()

传的参数都是 Trigger 那个对象 t 的地址

也就是说 现在我们有两个行动者将负责响应

也就是说 现在我们有两个行动者将负责响应

那个触发器对象 t 的值变更事件

那么我们现在就为 t 这个对象

设定一个新值 10 我们传递 s 字符串

在这里要把 s 字符串首先做一次常量转型

在这里要把 s 字符串首先做一次常量转型

把它的 const 给去掉

然后再做一次复诠转型

把它转成哑型指针作为附加参数传进去

虽然连续两次转型看上去很讨厌

也很麻烦

但是这是最科学最安全的方法

接下来我们 a2.Unlisten()

取消 a2 这个对象的侦听动作

这样的话 a2 这个对象

将从 t 这个对象的事件响应者列表中被删除

将从 t 这个对象的事件响应者列表中被删除

我们重新设定一次新值

然后你就能够看到

当第一次设定新值的时候

有两个对象去侦听它的值变更事件

而当取消 a2 这个对象的侦听之后

那么再次设定新值

将只有 a1 一个对象响应这个值变更事件

将只有 a1 一个对象响应这个值变更事件

这就是我们事件机制的全部实现

同学们一定要掌握它

尤其是指向类的成员函数的指针的用法

在一些复杂的程序架构里边

没有它是很难实现的

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1118笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。