STL_Adapter
¶1. 配接器之概观与分类
STL所提供的各种配接器中,改变仿函数(functors)接口者,我们称之为 function adapter,改变容器(containers)接口者,我们称之为 container adapter,改变迭代器(iterator)接口者,我们称之为 iterator adapter。
《STL源码剖析》 – P425
¶1.1 container adapters
STL提供的两个容器 queue 和 stack,其实都不过是一种配接器,他们修饰 deque 的接口而成就出另外一种容器风貌。
《STL源码剖析》 – P425
¶1.2 iterator adapters
STL 提供了许多应用于迭代器身上的配接器
- insert iterators
- back_insert_iterator
- front_insert_iterator
- insert_iterator
- reverse iterator
- iostream iterator
- istream_iterator
- ostream_iterator
《STL源码剖析》 – P425 - P428
¶1.3 function adapters
functor adapter 是所有配接器中数量最庞大的一个族群,其配接灵活度也是前两者不能及的,可以配接、配接、再配接,这些配接操作包括绑定(bind)、否定(negate),组合(compose),以及对一函数或成员函数的修饰(使其成为一个仿函数)
《STL源码剖析》 – P428 - P433
¶2. container adapters
¶2.1 stack
stack 的底层由 deque 构成,从以下接口可以清楚的看出 stack 与 deque 的关系:
1 | template <class T, class Sequence = deque<T>> |
《STL源码剖析》 – P434
¶2.2 queue
queue 的底层由 deque 构成,从以下接口可以清楚的看出 stack 与 deque 的关系:
1 | template <class T, class Sequence = deque<T>> |
《STL源码剖析》 – P434
¶3. iterator adapters
¶3.1 insert iterators
下面三种 insert iterators 内部都维护了一个容器(必须由用户指定);容器当然需要拥有自己的迭代器。于是,当客户端对 insert iterators 做赋值(assign)操作时,就在 insert iterators 中被转为对该容器的迭代器做插入(insert)操作,也就是说,insert iterators 的 operator=
操作符中调用底层容器的 push_back()
,push_front()
, insert()
操作函数。至于其他迭代器通常行为如 operator++
,operator++(int)
,operator*
都被关闭功能,更没有 operator--
, operator--(int)
, operator->
等功能,所以被定义为 output_iterator_tag。
《STL源码剖析》 – P435
¶3.1.1 back_insert_iterator
1 | template <class Container> |
《STL源码剖析》 – P435 - P436
¶3.1.2 front_insert_iterator
1 | template <class Container> |
需要注意的是,底层的容器要支持迭代器,并且支持 push_front()
的操作。
《STL源码剖析》 – P436
¶3.1.3 insert_iterator
1 | template <class Container> |
《STL源码剖析》 – P437
¶3.2 reverse iterator
所谓的 reverse iterator,就是将迭代器的移动行为倒转。如果 STL 算法接受的不是一般正常的迭代器,而是这种逆向的迭代器,它就会以从尾到头的方向来处理序列中的元素。如 rbegin()
和 rend()
。
注意:单项序列容器如 slist 不可以使用 reserve iterator,有些容器如 stack,queue,priority_queue 并不提供 begin()
,end()
,当然也没有 rbegin()
和 rend()
。
1 | // # reverse_iterator |
正向迭代器与对应的逆向迭代器取出不同元素,这个并不是一个潜伏的错误,而是一个刻意为之的特征,主要配合迭代器区间的“前闭后开”习惯,如下图所示。
小例子:
1 | deque<int> id{1, 0, 1, 2, 3, 4, 0, 1, 2, 5, 3} |
每个步骤的移动见下图所示:
《STL源码剖析》 – P437 - P442
上述的小例子来自于 P441 ↑
¶3. 3 stream iterator
所谓 stream iterator,可以将迭代器绑定到一个 stream 对象上,绑定到 istream 对象者,称之为 istream_iterator,拥有输入能力;绑定到 ostream 对象者,称之为 ostream_iterator,拥有输出能力。
¶3.3.1 istream_iterator
所谓绑定一个 istream object,其实就是在 istream iterator 内部维护一个 istream member,客户端对于这个迭代器所作的 operator++
操作,会被导引调用迭代器内部所含的那个 istream member 的输入操作(operator>>
),这个迭代器的类型是 input_iterator_tag,不具备 operator--
。
1 | // $ stream: input_stream |
使用方法:
1 | istream_iterator<int> eos; // (A) 造成 end_marker 为 false; |
通过以上代码可以知道,只要客户端定义一个 istream iterator 并绑定到某个 istream 对象上,程序便立刻停在 istream_iterator<T>::read()
函数,等待输入,这个并不是我们所预期的行为。因此,请在绝对必要的时刻才定义你所需要的 istream iterator。
《STL源码剖析》 – P442 - P445
¶3.3.2 ostream_iterator
所谓绑定一个 ostream object,其实就是在 ostream iterator 内部维护一个 ostream member,客户端对于这个迭代器所作的 operator=
操作,会被导引调用迭代器内部所含的那个 ostream member 的输入操作(operator<<
),这个迭代器的类型是 output_iterator_tag。
1 | template <class T> |
使用方法:
1 | ostream_iterator<int> outiter(cout, ' '); // ~ 输出至 cout, 每次间隔一个空格 |
《STL源码剖析》 – P445 - P447
¶3.3.3 stream_iterator 与 copy()
合作
以下是 stream iterator 与 copy()
合作的小例子,由以下两个例子结合
《STL源码剖析》 – P444 istream_iterator 与
copy()
合作的例子《STL源码剖析》 – P447 ostream_iterator 与
copy()
合作的例子
实现的功能:手动实时地向 vector 中输入元素,并最后每个元素以一个空格为间隔输出。
书中两个例子如下:
两者相结合测试用例如下:
1 |
|
实时输入:
输出结果1:(只要输入不是 int 就会退出)
输出结果2:
回车或者空格都可以作为判断截取数据,输出结果1以 end 退出,输出结果2以1212123221222一个超限的大数退出。
¶4. function adapter
¶4.1 not1, not2
¶4.1.1 一元
1 | // $ 对仿函数返回值进行否定配接器 |
使用:
1 | //not1 example |
《STL源码剖析》 – P451
STL之仿函数实现详解 <-- 例子来源
¶4.1.2 二元
1 | template <class Predicate> |
使用:
1 | // not2 example |
《STL源码剖析》 – P451
STL之仿函数实现详解 <-- 例子来源
¶4.2 bind1st, bind2nd
¶4.2.1 bind1st
1 | template <class Operation> |
《STL源码剖析》 – P452
¶4.2.2 bind2nd
1 | template <class Operation> |
《STL源码剖析》 – P452 - P453
¶4.2.3 使用
下面的例子,使用了 binder1st
,与 binder2nd
,并分别通过:① binder1st
直接生成函数对象和② 辅助函数 bind2nd
生成函数对象,实现了不同的功能,前者找出负数的个数,后者找出正数的个数。
1 | // binder1st example |
例子改写于 --> STL之仿函数实现详解
¶4.3 compose and compose2
¶4.3.1 compose
1 | // ! 将两个仿函数合并成一个仿函数的适配器 |
《STL源码剖析》 – P453
¶4.3.2 compose2
1 | // ! 二元仿函数合成操作 h(x) = f(g1(x), g2(x)) |
《STL源码剖析》 – P453 - P454
¶4.4 用于函数指针:ptr_fun
¶4.4.1 将函数指针包装成仿函数的配接器
具体的代码见:
《STL源码剖析》 – P455
使用:
1 | // # ptr_fun example |
STL之仿函数实现详解 <-- 例子来源
¶4.4.2 将成员函数指针包装成仿函数的配接器
具体的代码及相关内容见:
《STL源码剖析》 – P456 - P460
使用:
mem_fun
写法:
1 | // @ mem_fun example |
mem_fun_ref
写法:
1 | // @ mem_fun example |
《STL源码剖析》 – P456 - P460
STL之仿函数实现详解 <-- 例子来源
5 参考资料
《STL源码剖析》
C++之ptr_fun、mem_fun 和 mem_fun_ref
MiniSTL / Function 部分
MiniSTL / Iterator 部分
MiniSTL / SequenceContainers / queue,stack 部分