前面探讨了模板的经典用途: 建立容器类
- 描述一个或一组程序的接口的通用方式
- 使用模板提供抽象接口,可以写出独立于任何一种实际类型的函数
问题
系统的在概念上具有不同特性的部分能谨慎地分隔开,有利于(代码)复用
小例子
- 一个将整数数组元素相加的函数
|
|
- 使用这个函数:
|
|
sum函数知道三件事:
- 它把一组数加在一起
- 它所加的数是整数
- 它所加的整数以一种特殊的方式存储了起来
划分这个程序使把每一个特征分离到不同的部分中
分离迭代方式
函数的一个依赖: 添加的元素是放在数组中
用类抽象这种依赖: 迭代器
- 一个构造函数用来创建要处理的数据
- 请求序列中下一个元素的方法
- 告知遍历元素何时完成的方法
- 赋值操作符、复制构造函数、析构函数,使对象可以当作值使用
|
|
- 改写sum函数
|
|
不需要再直接知道正在添加的元素是如何保存的: 封装在Int_iterator中
使用新的函数:
|
|
- 实现未泛型化的迭代器
|
|
遍历任意类型
- 通用的Iterator模板
|
|
typedef Iterator<int> Int_iterator
使Int_iterator类型等价于Iterator<int>
增加其他类型
求出任意类型的值的和
- 将sum函数做成模板
|
|
- 类型T满足:
- 可以把0转换成该类的对象
- 对该类的对象定义类
- 对象具有类似值的语义,sum函数可以把对象作为值返回
存储技术抽象化
该迭代器只能访问存储在数组中的值
值可以保存在链表、文件中
需要反映不同数据结构的不同迭代器
把Iterator类转变成一个抽象基类,可以表示许多不同类型的迭代器类中的任何一个
|
|
Array_iterator<T>
就是一种Iterator<T>
:
|
|
- sum函数接受指向Iterator的引用作为参数以允许动态绑定
|
|
- 调用sum时将迭代器传给它
|
|
- 不能使用如下的调用方式:
cout << sum(Array_iterator<int>(x, 10)) << endl
- 子表达式
Array_iterator<int>(x, 10)
不是左值,没有绑定其上的非const引用
- 子表达式
每次遍历sum函数中的内部循环时都需要调用虚函数: 动态绑定(开销较大,尤其对象复杂时)
- 可以取消动态绑定减少额外开销,但需要在编译时知道相加元素类型
另一种实现方式(不采用继承)
- 让sum函数有两种类型的参数: 迭代器类型以及被加对象的类型
|
|
- 并不奏效: 定义了一个返回类型与参数无关的函数
sum(x)
的类型独立于x的类型(可能在C++中非法)
- 需要检查大量上下文条件才有办法判断表达式的类型
solution: 定义sum接收一个对求和结果的引用
|
|
- 重写main程序
|
|
- 可以通过保留sum接口使得原来的主程序可以运行
|
|
实证
利用sum2对一个从istream中生成的、数量不限的数字集合求和
可以当做迭代器的类: Reader
Reader<T>
对象从一个istream中读取一个T值的序列
必须在读取数据前检查istream中是否还有数据存在
- 从istream中读取数据看读取是否有效
|
|
- 每个Reader对象绑定一个给予构造函数的istream的引用
Reader<double>(cin)
是一个从cin中取回double值的Reader对象
从输入中读取的数字相加
|
|
Summary
- 任何一个大规模系统的关键在于将它划分成可以独立处理的小模块
- 在小模块之间定义清晰的接口
学生注册系统:
- 窗口接口 + 数据库接口
|
|
- 只要wizzy_window满足窗口系统的规范,dazzling_DB满足数据库系统的规范
《C++沉思录(Cplusplus Thinking)》笔记