特别说明:本文引用
[1]内容组织主要整理于 学堂在线的清华大学《C++语言程序设计进阶 (自主模式)》课程的 泛型编程章节 课程地址
[2]扩充概念主要参考 候捷的 《STL源码剖析》以及Matthew H.Austern.《Generic Programing and STL》
[3]部分参考百度百科
文章目录
0. 泛型程序设计(GP)的基本概念
- 编写不依赖于具体数据类型的程序
- 将算法从特定的数据结构中抽象出来,使其通用
- C++ 的模板为泛型程序设计奠定了关键基础
- 与面向对象的区别:OOP关注的是编程的数据方面,而泛型编程关注的是算法、
0.0 术语:概念
- 用来界定具备一定功能的数据类型,列如
- “可比较大小的所有数据类型(有比较有比较运算符)”----Comparable 概念
- “具有共有的复制构造函数并可以用=复制的数据类型”----Assignable 概念
- “Comparable,且是Assignable”----Sortable 概念
- 子概念:若A概念包含了B概念的含义,则称A是B的子概念,例如:
- Sortable 是 Comparable的子概念,也是Assignable的子概念
0.1 术语:模型(Model)
- 模型:符合一个概念的数据类型 称为 该概念的模型,例如:
- int型是Comparable概念的模型
- 静态数组类型不是Assignable概念的模型(无法用=给整个静态数组赋值)
0.2 用概念做模板参数名(联系到STL)
- 很多STL的实现代码就是使用概念来命名模板参数的
- 为概念赋予一个名称,并使用改名称作为模板参数名
- 例如,下述代码
template <class Sortable>
void insertionSort(Sortable a[], int n);
//插入排序算法函数,用Sortable作为模板参数名,意味要求,待排序的数组arr需要时Sortable概念的模型
1. STL简介(Standard Template Library)
1.1 STL 概要
- 标准模板库(STL)定以了一套概念体系,为泛型程序设计(Generic Pragramming)提供了逻辑基础
- STL中的各类模板,函数模板的参数都是用这个体系中的概念来规定的。
- 使用STL的模板时,类型参数既可以是C++标准库中得已有类型,也可以是自定义的类型(要求自定的类型是所要求的概念的模型)
1.2 STL的六大基本组件及其关系
- 容器(Container)
- 迭代器(Iterators)
- 函数对象(Function object/Functor):可以协助Algorithm完成策略变化
- 算法(Algorithms):通过Iterator存取Container
- 适配器(Adapters) :可以修饰或者套接Functor
- 配置器(Allocators)
- C++标准STL以源码形式供应,而非二进制可执行文件,且STL有不同的实现版本:HP, P.J.Plauger, Rouge Wave, STLport, SGI STL
- 头文件安排:13个头文件:<algorithm.>、<deque.>、<functional.>、<iterator.>、<vector.>、<list.>、<map.>、<memory.h>、<numeric.>、<queue.>、<set.>、<stack.>和<utility.>
- C++ 标准规定所有文件不再有拓展名,但是为了向下兼容,有些编译器同时提供<vector.h>和<vector.>两种形式
1.3 基本组件——容器(Container)
此处说明其框架结构关系,具体实现和特点参考数据结构
- 容纳、包含一组元素的对象。
- 基本容器类模板
1.3.1 容器分类
容器类别 | 具体容器 | 功能 |
---|---|---|
顺序容器 | array(数组)(长度不可扩展)、vector(向量)、deque(双端队列)、forward_list(单链表)、list(列表) | 看做长度可扩展的数组 |
(有序)关联容器 | set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射) | |
无序关联容器 | unorderedset (无序集合)、unorderedmultiset(无序多重集合)unorderedmap(无序映射)、unordermultimap(无序多重映射) | |
容器适配器 | stack(栈)、queue(队列)、priority_queue(优先队列) |
1.3.2 容器功能
- 容器的通用功能
- 用默认构造函数构造空容器
- 支持关系运算符:==、!=、<、<=、>、>=
- begin()、end():获得容器首、尾迭代器
- clear():将容器清空
- empty():判断容器是否为空
- size():得到容器元素个数
- s1.swap(s2):将s1和s2两容器内容交换
- 相关数据类型(S表示容器类型)
- S::iterator:指向容器元素的迭代器类型
- S::const_iterator:常迭代器类型
1.3.3 可逆容器的访问
- STL为每个可逆容器都提供了逆向迭代器,逆向迭代器可以通过下面的成员函数得到:
- rbegin() :指向容器尾的逆向迭代器
- rend():指向容器首的逆向迭代器
- 逆向迭代器的类型名的表示方式如下:
- S::reverse_iterator:逆向迭代器类型
- S::constreverseiterator:逆向常迭代器类型
1.3.4 随机容器的访问
- 随机访问容器支持对容器的元素进行随机访问
- s[n]:获得容器s的第n个元素
1.3.5 顺序容器功能接口
- 构造函数
- 赋值函数
- assign
- 插入函数
- insert, pushfront(只对list和deque), pushback,emplace,emplace_front
- 删除函数
- erase,clear,popfront(只对list和deque) ,popback,emplace_back
- 首尾元素的直接访问
- front,back
- 改变大小
- resize
STL所提供的顺序容器各有所长也各有所短,我们在编写程序时应当根据我们对容器所需要执行的操作来决定选择哪一种容器。
如果需要执行大量的随机访问操作,而且当扩展容器时只需要向容器尾部加入新的元素,就应当选择向量容器vector;
如果需要少量的随机访问操作,需要在容器两端插入或删除元素,则应当选择双端队列容器deque;
如果不需要对容器进行随机访问,但是需要在中间位置插入或者删除元素,就应当选择列表容器list或forward_list;
如果需要数组,array相对于内置数组类型而言,是一种更安全、更容易使用的数组类型。
1.3.5 关联容器功能接口
- 关联容器的特点
- 每个关联容器都有一个键(key)
- 可以根据键高效地查找元素
- 接口
- 插入:insert
- 删除:erase
- 查找:find
- 定界:lowerbound、upperbound、equal_range
- 计数:count
四种关联容器
单重关联容器(set和map)
键值是唯一的,一个键值只能对应一个元素
多重关联容器(multiset和multimap)
键值是不唯一的,一个键值可以对应多个元素
简单关联容器(set和multiset)
容器只有一个类型参数,如set、multiset,表示键类型
容器的元素就是键本身
二元关联容器(map和multimap)
容器有两个类型参数,如map<K,V>、multimap<K,V>,分别表示键和附加数据的类型
容器的元素类型是pair<K,V>,即由键类型和元素类型复合而成的二元组
无序关联容器
C++11新标准中定义了4个无序关联容器
unorderedset、unorderedmap、unorderedmultiset、unorderedmultimap
不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符。
提供了与有序容器相同的操作
可以直接定义关键字是内置类型的无序容器。
不能直接定义关键字类型为自定义类的无序容器,如果需要,必须提供我们自己的hash模板
1.4 基本组件——迭代器(Iterator)
1.4.1 概念特性
- 迭代器是泛化的指针,提供了顺序访问容器中每个元素的方法
- 提供了顺序访问容器中每个元素的方法;
- 可以使用“++”运算符来获得指向下一个元素的迭代器;
- 可以使用“*”运算符访问一个迭代器所指向的元素,如果元素类型是类或结构体,还可以使用“->”运算符直接访问该元素的一个成员;
- 有些迭代器还支持通过“–”运算符获得指向上一个元素的迭代器;
- 迭代器是泛化的指针:指针也具有同样的特性,因此指针本身就是一种迭代器;
- 使用独立于STL容器的迭代器,需要包含头文件
- 迭代器是算法和容器的桥梁
- 迭代器用作访问容器中的元素
- 算法不直接操作容器中的数据,而是通过迭代器间接操作
- 算法和容器独立
- 增加新的算法,无需影响容器的实现
- 增加新的容器,原有算法也能适用
1.4.2 迭代器分类
1.4.3 迭代器支持的操作
- 迭代器是泛化的指针,提供了类似指针的操作(诸如++、*、->运算符)
- 输入迭代器
- 可以用来从序列中读取数据,如输入流迭代器
- 输出迭代器
- 允许向序列中写入数据,如输出流迭代器
- 前向迭代器
- 既是输入迭代器又是输出迭代器,并且可以对序列进行单向的遍历
- 双向迭代器
- 与前向迭代器相似,但是在两个方向上都可以对数据遍历
- 随机访问迭代器
- 也是双向迭代器,但能够在序列中的任意两个位置之间进行跳转,如指针、使用vector的begin()、end()函数得到的迭代器
1.4.5 迭代器的区间
- 两个迭代器表示一个区间:***[p1, p2)***
- STL算法常以迭代器的区间作为输入,传递输入数据
- 合法的区间
- p1经过n次(n > 0)自增(++)操作后满足p1 == p2
- 区间包含p1,但不包含p2
1.4.5 迭代器的辅助函数
- advance(p, n)
对p执行n次自增操作 - distance(first, last)
计算两个迭代器first和last的距离,即对first执行多少次“++”操作后能够使得first == last
1.5 基本组件——函数对象(Function Object)
- 一个行为类似函数的对象,对它可以像调用函数一样调用。
- 函数对象是泛化的函数:任何普通的函数和任何重载了“()” 运算符的类的对象都可以作为函数对象使用
- 可以没有参数,也可以带有若干参数
- 如
- 普通函数就是函数对象
- 重载了“()”运算符的类的实例是函数对象
- 使用STL的函数对象,需要包含头文件
1.5.1 STL提供的函数对象
用于算术运算的函数对象:
一元函数对象(一个参数) &#x