第三讲
stl六大部件:算法是函数模板,其他的是类模板
算法形式:传入两个迭代器(第三个参数可能有:一个比较的准则
算法需要的所有信息从迭代器获取
迭代器分类
基于红黑树的结构是双向迭代器;
基于hash的取决于:每个bucket是单向还是双向;
关于istream_iterator的迭代器分类:都将自己的迭代器类型定义为input_iterator_tag
(ostream_iterator相同)
迭代器分类对算法的影响:distance、advance;
关于copy:
右侧的type traits:第四章
相同的例子,视频中还有destroy
unique_copy
:
重点区分左右两个版本的区别及其原因:右侧的
*result != *first
是read-op,如果传入的输出迭代器,引发错误!因此需要重载出左边的版本:找一个中间变量value来读。
调用算法时,注意迭代器类别的“暗示”的重要性:以sort
为例,模板参数名称是RandomAccessIterator
,因此一定要传入该类型迭代器。尽管其他类型迭代器也不会引发错误,但是不能使算法发挥作用。其他的:
算法源代码剖析
涉及算法:
- accumulate
- for_each
- replace(xxx_if/xxx_copy)
- count/count_if
- find/find_if
- sort
- binary_search
使用count类、find类、sort类要注意:尽量使用容器自身的实现版本
关联容器有自己的count、find类;没有sort类(因为自身能够保持有序)
list和forward_list有自己的sort(stl的sort要求随机访问迭代器)
提到反向迭代器:reverse_iterator()
是迭代器适配器
二分查找需要先保证有序
仿函数functors
根据前面的六大部件的关系图,可以看出,仿函数只服务于算法
stl中三种functors的形式:
一个class模板参数;
重载()
运算符GNU C++独有:identity、select1st、select2nd
黄色阴影:stl的functor有继承关系
functor给算法提供服务的示例:
“没有融入stl”:自己写的functor没有继承关系,会有一些使影响(不能被改造)
functors的可支配条件:需要继承适当的东西,如unary_function
/binary_function
Adapter
特点:
binder2nd
-
一个主体class + 一个小的辅助函数(以binder2为例,主体class是
binder2nd
,可供调用的小的函数是bind2nd
) -
传入的数据类型,会先被记下来,以备后续使用
调用count_if时,传入的第三个参数
pred
(bind2nd(less<int>(), 40)
)后,先把less<int>()
、40
记下(分别对应binder2nd
中的op
和value
),在count_if使用过程中真正用到pred时,调用到binder2nd中的()
才会真正使用这两种先前被记住的类型 -
仿函数适配器:修饰functor,生成的结果也应该是functor,因此第1点中提到主体class也要重载
()
-
一些细节:见上图
-
提前记住类型名,有
typename
的原因:一些类型在编译还未使用,所以要让编译器先同意这种类型合法
视频中提到,GNU自带的非c++标准的适配器有:compose1、compose2
bind
c++11取代binder2nd的:bind
具体用法:《C++ Prime 5th》P354/cplusplus
bind可以指定一个返回值类型的模板参数
迭代器适配器
逆向迭代器
实现的部分:reverse_iterator
插入迭代器:inserter
重点理解:
视频中讲述的,如何在不改变copy算法的源码,通过操作符重载(()
),结合inserter
实现任意位置的插入
其实是,对于copy,传入的迭代器适配器不同,进行的操作就不同,进一步说明迭代器适配器服务于算法的深刻含义
ostream_iterator/istream_iterator
看视频的思考:
- 可以理解ostream迭代器为什么设置:
=
、*
、++
等 - 关于istream迭代器:在创建某个带参的istream迭代器时,就已经做了一次读入