10.1概述
大多数定义在头文件里,部分数值泛型算法定义在里。
算法不依赖于容器类型,但依赖于元素类型操作(即关系运算)。
对于内置数组,可以使用 begin(数组名)、end(数组名)来获取对应指针,使算法对内置数组进行类似容器的处理。
算法并不真正执行容器本身操作,一切操作都基于迭代器。
10.2简单泛型算法的使用
只读算法
count
- 接收:一对迭代器和一个值。
- 返回:该值出现的次数。
find
- 接收:一对迭代器和一个值。
- 返回:迭代器。
accumulate
- 接收:一对迭代器和一个初值。
- 返回:累加求和结果。
- 误区:string可以实现,但const char* 不可以实现。
equal
- 接收:第一个序列的一对迭代器和第二个序列的首迭代器。
- 返回:true/false。
- 误区:string可以实现,但const char* 不可以实现。
写算法
fill
- 接收:一对迭代器和一个用于赋值的值。
fill_n
- 接收:一个迭代器表示开始位置,一个值表示赋值个数,一个值用于赋值。
- 误区:不能给空容器赋值。
back_inserter
- 头文件:iterator
- 功能:向容器末尾插入元素(结合fill_n可解决空容器问题)。
- 例子
vector<int>vec; auto it=back_inserter(vec); fill_n(it,10,0);
- 重排算法
- unique
- 接收:一对迭代器。
- 返回:指向重排后不重复区域后一个位置的迭代器。
- 和erase方法配套可以去重。
- unique
10.3 自定义操作
谓词:一个可调用的表达式,返回结果为一个能用作条件的值,分一元谓词与二元谓词(也就是自定义取代默认操作符)。
可调用对象
- 定义:可以对其使用调用运算符(())的对象或表达式。
- 包括:函数与函数指针、重载函数调用运算符的类、lambda表达式。
lambda表达式
- 定义:可调用的代码单元,类似于未命名的内联函数,格式为:
[capture list](parameter list)−>return type {function body} - 特点:
- 可定义在函数内部。
- 必须使用尾置返回,但返回类型与参数列表可忽略,同时自动推断返回类型(若包含单一return之外的语句则返回void)。
- 不能有默认参数。
例子
auto f = [](const string &a, const string &b) {return a.size() < b.size();}; stable_sort(s.begin(), s.end(), f());
或者直接
stable_sort(s.begin(), s.end(), [](const string &a, const string &b) {return a.size() < b.size();});
捕获方式:
- 显示捕获
- 值捕获
- 如果想在lambda表达式中改变该值,须在参数列表后,表达式体前加关键字 mutable。
- 引用捕获
- 值捕获
- 隐式捕获:只写明捕获方式而不写明捕获的具体对象。
- 显示捕获
- 定义:可调用的代码单元,类似于未命名的内联函数,格式为:
函数替代lambda表达式
思路:运用bind函数
bind函数
- 头文件:functional
- 一般形式:
auto newCallable=bind(callable, arg_list)
其中callable为相关函数,arg_list为逗号分隔的 参数列表。
- arg_list
- _n:占位符,表示绑定函数的第n个参数。
- 通常为_n与实参混合使用。
- 其中_n必须显示声明using namespace std::placeholders。
- 一个巧妙运用
sort(words.begin(), words.end(), bind(isShorter, _2, _1));
- 运用ref()函数绑定参数实现引用传递。
10.4 头文件中的迭代器
插入迭代器:back_inserter、front_inserter、inserter。
IO迭代器:
istream_iterator
- 如果绑定流则从流的第一个位置开始,不绑定则为尾后位置。
辅助创建容器
istream_iterator<int>in(cin),eof; vector<int>v(in,eof);
迭代器被解引用时才真正从流中读取数据。
ostream_iterator
- 通过绑定的可以为每次输出附加一个固定输出。
- 辅助容器输出(推荐未注释的方法)
for(auto e : v) out = e; // for(auto e : v) // *out++ = e; // copy(v.begin(), v.end(), out);
- 反向迭代器
会出现倒序输出的情况(有时来说是个问题),可以使用base()方法使反向迭代器转化为普通迭代器。
10.5 泛型算法结构
五类迭代器(层次由低到高)
输入迭代器
- 支持 ==、!=
- 支持前置、后置自增
- 支持解引用*与->运算符
- 只能用于单遍扫描,可支持算法find、accumulate,istream_iterator是输入迭代器。
输出迭代器
- 输入迭代器的补集
- 支持前置、后置自增
- 支持解引用运算符
- 出现在赋值运算符的左侧
- 支持copy的第三个参数,ostream_iterator是输出迭代器
- 前向迭代器
- 输入输出迭代器的并集
- 支持replace算法,forward_list的迭代器是前向迭代器
- 双向迭代器
- 支持前置、后置自减运算符
- 支持reverse算法
- 随机访问迭代器
- 比较运算符
- 单迭代器的加减运算符
- 两个迭代器的减运算符
- 下标运算符
- 支持sort,array,deque,string,vector都是随机访问迭代器,内置数组的指针也是。
接收单个目标迭代器的算法,一般可以用输入迭代器代替写入目的位置的迭代器以保证安全。
算法命名规范
- 一些算法使用重载,接收谓词来代替==或<运算符。
- _if版本的算法,通过谓词来代替元素值。
- 一些算法增加_copy版本把新的结果存储到其他容器中
链表的特定算法
- merge:分治合并(有重载的谓词版本)
- remove:删除元素(有remove_if的谓词版本)
- reverse、sort、unique
- splice:将一个链表的一部分移动到另一个链表中,其中若调用者为双向链表,则移动到位置之前,否则为位置之后。
- 链表特有的算法会改变底层容器。