当前课程知识点:基于Linux的C++ > 第十一讲 泛型编程 > 11.9 泛型编程实践(一) > LinuxCPP1109
接下来我们讨论泛型编程实践
我们首先介绍标准模板库
在完成标准模板库的基本介绍之后
我们讨论怎么编写函数模板、类模板
和怎么进行元编程
在此之后我们给出一个工程实践的例子
我们首先来看标准模板库
在 C++ 的标准模板库里面
提供了丰富的内容
包括一些特定的标准模板类
比如说复数、序偶 还有一些迭代器
有一些标准的容器 向量、表、栈、
队列、集合、映射等等
提供了好多种标准容器 你可以直接用
同时它还提供了一系列标准算法
包括查找、排序等等 你也可以直接用
对于标准模板库的算法
或者类的基本的使用
它有一个原则
因为它是模板的定义嘛
所以要用“〈〉”这样的方式来进行访问
一个典型的标准模板库里面的形式
首先是一个模板名称
后面跟着是一个“〈〉”
里面跟着就是数据对象的基型式
然后是后面的你定义的数据对象的名称
你比如讲
它定义了复数的一个类模板 complex
你可以以 double 类型、以 float 类型
来定义一个标准的复数
在定义这样的一个复数过程中
那么你就写 complex〈double〉
就表示 doulbe 类型的复数
后面你就可以定义复数类型的
这样一个对象了
就按照这个方式去使用
再比如讲 pair 就是序偶
在序偶里边 它需要提供两个基型式:
一个是 string 第二还是 string
我们就可以定义 name
传“Zhang”、“San”进去
就定义了一个序偶
前面是姓 后面是名字
就按照这个方式去定义一个序偶
当然了 对于序偶来讲
这两个型并不要求是一样的
第三个例子 我们定义一个向量
那么整型向量就写 vector〈int〉
后面写 v(8)
就表示定义 8 个元素的整型向量
就按照这个方式去使用
习惯了这个使用记号之后
你就会发现标准模板库的使用
其实是非常方便的
我们首先来看复数 复数 complex
它的一般性的说明
float、double 和 long double 都可以用
但首选是 double
float 的话 精度太低了 一般我们不用
long double 在很多编译器下面
其实已经舍弃了 就已经不用了
因为它的精度就比 double 高一点
实际上高得也有限
用处实际上不大
如果 double 精度都不够的话
long double 基本上也不够
所以实际上大部分时候我们就不用它
首选 double
在复数类里边
提供了实部和虚部的操作
实部用 real() 这个成员函数
虚部用 imag() 这个成员函数
实际上是 imaginary
虚部的意思 OK
用这两个成员函数
来访问它的实部和虚部
复数的话 它有一系列的数学操作
这些数学操作都已经重载了
你可以像数学一样去使用这个复数
使用起来实际上是非常非常方便的
cin 和 cout
这两个输入输出流实际上也已经重载了
这两个输入输出流实际上也已经重载了
使用的时候也就像普通的数据对象一样
去输入输出就可以了
只要你要记住 复数的输入输出流的格式
必须是“(real, imag)”
必须按照这个方式来输入输出
其它方式是不可以的
第二个类是序偶
序偶的话 这个模板的名字叫 pair
它用来构造两个对象
这两个对象形成一个前后的关系
序偶包括两个成员
一个是 first 一个叫 second
这两个成员实际上是公开的
如果你看标准模板库的定义的话
你就会发现序偶本身是用 struct 定义的
为什么叫序偶呢
因为这两个成员的前后关系是非常重要的
第一个就是第一个 第二个就是第二个
这两个是不颠倒的
所以特别注意这一条
当你定义像这样一个序偶的时候
你可以写 pair〈int, double 〉
这就意味着我定义了一个整型
和一个浮点型相构造的一个序偶 可以
像 pair〈 string, string〉
就构造两个字符串型的序偶
这也可以 name 这个我们刚才已经见过了
具体使用的时候 序偶的两个成员
每一个都可以直接访问然后直接输出
如果它们的型是已经预定义好了的
或者已经重载了流操作符
那么就可以直接输入输出
序偶可以比较大小 它先比较 first
它们的大小 谁大谁小
就能够决定下来
如果两者是相等的
那么接下来比较 second 的大小
它是按照这样方式进行比较的
如果要构造一个序偶
你可以使用 make_pair() 这个函数
这个函数方便你构造一个序偶
你只要传两个参数给它
它就会返回一个序偶
接下来是向量
向量的目的是为了替代数组
可以像数组一样使用向量
我们前面讲数组的时候特别谈到过
因为 C/C++ 那个数组的分配 它是静态的
它没有办法在程序运行期间
自动地适应这个数组的元素个数变化
所以我们才声明了一个动态数组的数据结构
也实现了一个动态数组类
就是这个目的
那么在 C++ 标准库里边呢
它提供了一个更灵活、更强大的机制
这个机制就是向量
你可以像数组一样地
使用 C++ 标准模板库里边的向量
它的相当多的运算都已经重载了
使用起来实际上是非常方便的
基本的向量的定义格式是这样的
vector〈int〉 v(8)
我们刚才不解释了吗
这将创建一个包含 8 个元素的整型向量
名字叫 v
注意这是一个 vector〈int〉这个类的对象
这个类的名字就叫 vector〈int〉
少了“〈int〉” 那不叫类的名字 特别注意
那是类模板的名字
要给它模板的实际参数
才能构造这个实际的类 叫 vector〈int〉
这是这个整型向量类的全称
你就用它来构造这个类一个对象 v(8)
8 个元素
operate[] 在向量上面已经重载了
所以你可以这样数组一样用“[]”
来访问它的第 i 号元
v(i) 访问的就是它的第 i 号元
和数组不同 向量允许你进行整体赋值
和数组不同 向量允许你进行整体赋值
这极大地方便了我们的编程
有一个成员函数叫 size()
它将返回向量中元素的数目
也就是现在这个向量中包含多少个元素
那么你就可以用 size() 这个成员函数来获得它
capacity() 用来获得的是向量的当前的容量
也就是说它可以容纳的最多的元素个数
向量的尺寸 size() 求出来的那个向量元素个数
可能和向量的容量并不相同
比如说它比容量少 这是有可能的
clear() 是清除这个向量中的全部的元素
但是并不销毁这个向量对象本身
resize() 就是重设这个向量的容量
你可以重新设定它
你觉得原来向量的尺寸不够用了
你就可以用 resize()
重新设定它的向量的尺寸
可以把它变大
如果你觉得原来多了
你也可以用 resize() 把它缩小
这都没有问题
接下来就是最重要的一个概念 迭代器
迭代器在 C++ 标准模板库里边
起到了一个核心的作用
那么什么叫迭代器呢
迭代器其实就是访问容器中的数据对象的一个技术手段
迭代器其实就是访问容器中的数据对象的一个技术手段
我们通过迭代器访问容器中的数据对象
它在功能上类似于指针
或者我们的数组的索引下标
我们通过指针加减运算和数组下标的运算
就可以很容易地访问到下一个元素
对吧 这是我们原来讲数组的时候
讲指针的时候谈到过的 迭代器呢
它在功能上就和这个非常类似
在实现上迭代器可以是指针
也可以不是指针 这个并不强求
另外它也并不要求必须是那个对象的地址
不 只要能够访问到那个对象
能够访问它的下一个对象 就 OK 了
不管是哪一种技术手段
只要能够访问到
它就能够起到迭代器的作用
这是非常重要的一个地方
迭代器的使用方法有一个特别的要求
首先 你必须声明一个迭代器变量
特别跟同学们强调
对于标准模板库的容器来讲
模板库中的每一个容器
都有与它相适应的迭代器
不同的容器 它的迭代器是不一样的
是不可以混用的 这是一个
第二个 你就使用引领操作符
来访问迭代器所指向的目标数据对象
在实现上 迭代器类似于一个指针
我们前面不讲了吗
但是它并不一定是指针
但不管怎么样 在迭代器上
它重载了引领操作符
重载了引领操作符以后
那么对于使用者而言
迭代器的表现形式就和指针是一样了
那么你就用那个迭代器
引领得到它的目标数据对象
迭代器本身是指向那个容器中的目标数据对象的
迭代器本身是指向那个容器中的目标数据对象的
所以你引领就得到了容器中的目标数据对象
那么这就是第二步
使用引领操作符访问迭代器所指向的目标数据对象
使用引领操作符访问迭代器所指向的目标数据对象
第三步 使用递增操作符获得下一个对象的访问权
第三步 使用递增操作符获得下一个对象的访问权
它重载了递增操作符
那么你就可以在迭代器上面
利用重载的递增操作符
获取下一个对象的访问权
一递增 迭代器就会指向下一个目标数据对象
你自然再引领
就能够访问到下一个数据对象了
第四个 若迭代器的新值
超出了容器的元素范围
类似于我们链表中的那个指针
最后变成 NULL 就表示链表结束了
那么这个时候 目标数据对象本身不可用
要特别注意这一个
从功能分类上来讲
迭代器包括了好几种
一种叫输入迭代器
对于输入迭代器来讲
它提供的是对象的只读访问
第二种是输出迭代器
它提供是对象的只写访问 它做输出
第三种是前向迭代器
它提供的是对象的正向
也就是递增的读写访问
只能从前到后 这叫前向迭代器
从前到后 你比如说对于一个数组来讲
就是从 0 号元 一直到 1 号元、2 号元
按照这个顺序
这就叫前向迭代器
有时我们也说正向迭代器
它是个递增的
还有一种就是双向迭代器
它提供的是一个对象的
正向与反向的双向访问
这个时候它不仅重载了递增操作符
它也重载了递减操作符
两个你都可以用
还有一种叫做随机访问迭代器
它提供的是对象的随机访问
你可以随机地读写它
那么这样的情况下
就可以支持你用下标来去访问它的元素
就可以支持你用下标来去访问它的元素
功能实际上是相当强大的
对于一个特定的容器来讲
它所使用到的迭代器 性质是不一样的
有的只有输出迭代器 有的只有输入迭代器
有的就只有前向 有的是有双向的
有的会提供随机访问迭代器
所以在使用的时候是需要特别注意的
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口