10. 泛型算法
可以用于不同类型的元素和多种容器类型(标准库类型比如vector和list,还包括内置的数组类型)
10.1 概述
迭代器令算法不依赖于容器,但算法依赖于元素类型的操作。
算法本身不会执行容器的操作,只会运行于迭代器之上执行迭代器的操作。
10.2 初始泛型算法
标准库提供的算法具有一致的结构,前两个参数(迭代器)表示范围(对该范围内的元素进行操作)。不同的算法对范围内的元素使用方式不同,通过算法是否读取元素、改变元素或是重排元素顺序了解不同的算法。
10.2.1 只读算法
本身不改变元素,例如find、count、accumulate(求和)、equal(判断是否相同)等函数。
10.2.2 写容器元素的算法
序列原大小要大于等于算法写入的元素数目,由于算法不会执行容器操作,因此自身不可能改变容器大小,例如fill(在指定范围内填充)、fill_n(填充指定数目个值)、copy、replace函数。
fill:
fill_n:
为了保证算法有足够空间容纳数据,可以使用插入迭代器back_inserter,它是定义在头文件iterator中的函数。通过插入迭代器可以向容器中添加元素。
常常使用back_inserter创建一个迭代器作为算法的目的位置使用。back_inserter接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器,使用此迭代器赋值时,赋值运算符会调用push_back将元素添加到容器中。因此最终fill_n语句向vec末尾添加了10个值为0的元素。
copy:
replace:
将所有等于第一个值的元素替换成第二个值:
如果希望保留原序列不变,可以调用replace_copy函数产生一个新的序列,其中提供一个参数指出调整后的序列的保存位置:
10.2.3 重排容器元素的算法
调整容器中元素的顺序,例如sort函数。例如已有一个vector,希望使得vector中每个单词只出现一次:
其中unique并不真的删除元素,只是覆盖相邻的重复元素。
10.3 定制操作
通过用户自己定义的操作来替代默认运算符。例如调用sort的过程中,希望的排序顺序与<定义的顺序不同(<是按照大小排序或者按照字典序排列),此时需要重载sort的默认行为。
10.3.1 向算法传递函数
例如希望单词按照长度排序,长度相同的再按字典序排列,因此需要使用sort的重载版本,该版本接受第三个参数,此参数是一个谓词。
谓词:是一个可调用的表达式,返回结果是一个能用作条件的值。其中一元谓词只接受单一参数,二元谓词有两个参数。
为了使相同长度的元素按字典序排列,可以使用stable_sort算法,稳定排序算法维持相等元素的原有顺序(elimDups在上一节):
10.3.2 lambda表达式
根据算法接受一元谓词还是二元谓词,算法必须严格接受一个或两个参数。lambda表达式是一种可调用对象(可调用对象包括函数、函数指针、重载了函数调用运算符的类以及lambda表达式),通过向算法传递一个可调用对象实现相应功能。
与isShorter函数完成相同功能的lambda:
空的捕获列表表名此lambda不适用它所在函数中的任何局部变量,使用此lambda调用stable_sort如下:
另一个例子是求大于等于一个给定长度的单词有多少,将此函数命名为biggies:
获取指向第一个满足size()>=sz的元素的迭代器:
可以使用find_if算法查找第一个具有特定大小的元素,find_if算法接受一对迭代器表示一个范围
计算满足size>=sz元素的数目:
利用find_if返回的迭代器计算从它到words的末尾一共有多少个元素
打印长度大于等于给定值的单词:
使用for_each算法,此算法接受一个可调用对象并对输入序列中每个元素调用此对象。
最后,完整的biggies函数如下:
10.3.3 lambda捕获和返回
10.3.4 参数绑定 bind
10.4 再探迭代器
标准库在头文件iterator中还定义了额外几种迭代器:
10.4.1 插入迭代器
插入迭代器被绑定到一个容器上,可用来向容器插入元素。
插入迭代器有三种类型,差异在于元素插入的位置:
10.4.2 流迭代器
流迭代器被绑定到输入或输出流上,可用来遍历所关联的IO流。
istream_iterator读取输入流,ostream_iterator向一个输出流写数据。这两种迭代器将对应的流当作一个特定类型的元素序列来处理。
10.4.3 反向迭代器
反向迭代器向后而不是向前移动,除了forward_list之外的标准库容器都有反向迭代器。对于反向迭代器,递增递减操作的含义会颠倒过来,递增(++it)一个反向迭代器会移动到前一个元素,递减(it--)一个反向迭代器会移动到下一个元素。
10.4.4 移动迭代器
移动迭代器不是拷贝其中的元素,而是移动它们。
10.5 泛型算法结构
算法所要求的迭代器操作可以分为5个迭代器类别,每个算法都对其迭代器参数指明需要哪一类:
10.6 特定容器算法
list和forward_lisy定义了几个成员函数形式的算法,定义了独有的sort、merge、remove、reverse和unique:
链表类型还定义了链表数据结构特有的spice算法: