适配器Adaptor
适配器
1、背景
适配器在STL组件中,扮演者轴承、转换器的角色。
- 对象A改造了对象B,此时A就代表了B然后被大家使用,但是A做的主要事情又是依托B去做的,因为B是本来就有的东西。所以A是使用的桥梁,而B是幕后的工作者。在程序使用上,如果A要取用B的功能,第一种方式就是继承
(A is B)
,另一种就是内含方式(A has B)
。这里的Adaptor都是采用内含方式。 - 例如我们笔记本的电源,一般都会有一个适配器把220v的电压降到适合笔记本工作的电压范围,这样笔记本就可以工作在我们常用的电压环境了,这就扩大了笔记本的使用场景,在软件开发过程中也是一样的道理。
2、定义
将一种容器或迭代器装换或封装成另一种容器或迭代器,即将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运作。
- 适配器内部有一个原来要适配的成员变量,通过改变接口来实现
这里以仿函数适配器为例,简单介绍一下,适配器扮演的角色: - 1、算法的功能就是实现相关的逻辑行为,例如排序sort、查找find、比较less等等,而这些逻辑行为的实现需要借助一些仿函数作为组织生产资料的包工头,这里的生产资料就是数据(存在容器之中);
- 2、在很多时候,一项任务的实现,单一的的仿函数(包工头) 可能无法完成整个任务,所以项目经理(算法)需要组织多个仿函数结合使用(如上图的程序,查找大于40的元素)
- 3、适配器是一种合作模式,它在原有类型的基础上扩展成为另外一个接口,使原本因为接口不兼容而不能合作的类型可以一起工作。
3、类型
- 1、容器适配器(container adaptor)
- stack
- queue
- 2、迭代器适配器(iterator adaptor)
- 反向迭代器(reverse_iterator)
- 安插型迭代器(inserter)
- 流迭代器(istream_iterator / ostream_iterator)
- 移动迭代器(move_iterator)
- 3、仿函数适配器(function adaptor)
- bind1st,bind2nd(适配器绑定)
- not1()、not2()(函数的返回值取否)
- compose1()、compose2()(仿函数合并)
3.1、容器适配器(container adaptor)
适配器就是以序列式容器为底层数据结构,进一步封装了的为适应场景应用的容器。例如:低层由deque构成的stack和queue,其基本实现原理是,
- 在
stack
和queue
内部定义一个protected
的deque
类型的成员变量, 然后只对外提供deque
的部分功能或其异构,如stack
的push
和pop
都是从deque
的尾部进行插入和删除; queue
的push
和pop
分别是从尾部和头部进行插入和删除;stack
和queue
都没有迭代器,因此不能对stack或queue进行遍历。但他们提供了operator ==
和operator<
这两个比较大小的操作符:
这样 stack 和 queue 都可以看做是适配器,作用于容器 deque 之上的适配器。
容器适配器及其基础容器表格
容器适配器 | 基础容器筛选条件 | 默认使用的基础容器 | 可适配类型 | 配条件 |
---|---|---|---|---|
stack | 基础容器需包含以下成员函数:(1)empty( ) (2)size( ) (3)back( ) (4)push_back( ) (5)pop_back() | deque | vector,list,deque | 任意顺序容器 |
queue | 基础容器需包含以下成员函数:(1)empty( )(2)size( ) (3)back( ) (4)front( )(5)push_back( ) (6)pop_back() | deque | list ,deque | 必须提供push_front运算(所以不支持vector) |
priority_queue | 基础容器需包含以下成员函数:(1)empty( ) (2)size( ) (3)front( ) (4)push_back( ) (5)pop_back() | vector | vector,deque | 随机访问 |
为什么stack不默认使用vector/list作为底层容器呢?
- 原因是因为vector在扩容的时候有较差的时间复杂度,而list虽然有平稳的时间复杂度,但是总体平均复杂度较高
3.1.1、stack
由于deque可以从首位两端插入或剔除元素,所以只需要对其进行简单的封装就可以分别实现先进先出(FIFO)的stack和先进后出(FILO)的queue了。
- stack只提供从末端插入和删除的接口以及获取末端元素的接口
template <class T, class Sequence = deque<T> >
class stack {// 原型声明
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c;//Sequence为deque<T>,c为实际存储数据的容器
public:
// 向外部提供的接口,都是调用deque的接口来实现的
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
3.1.2、queue
queue则只提供从尾部插入而从头部删除的接口以及获取首位元素的接口.
template <class T, class Sequence = deque<T> >
class queue {
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c;
public:
// 实际上也是对Sequence的封装
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
const_reference front() const { return c.front(); }
reference back() { return c.back(); }
const_reference back() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
3.2、仿函数适配器(function adaptor)
container adaptor
内含一个container
的成员,然后对这些成员的标准接口进行了一定的改造,从而使之变成一个新的 container,满足新的应用环境的要求。- 仿函数的适配器也是类似的,其实就是在
function adaptor
内部定义了一个成员变量,它是原始functor
的一个对象. - 这些配接器包括
系结(bind)
、否定(negate)
、组合(compose)
、以及对一般函数或成员函数的修饰,几乎可以无限制地创造出各种可能的表达式,搭配算法一起使用。 - 仿函数适配器的实现主要包括两块,自身的类以及方便使用的函数。
3.2.1、bind1st,bind2nd(适配器绑定)
3.2.1.1、bind2nd
bind2nd()
它的作用是绑定仿函数的第二参数,它本身就是一个仿函数的适配器,它的作用是为我简化binder2nd()
//从它继承自unary_function即可得知它也是仿函数
//Operation前面讲解的仿函数类
template <class Operation>
class binder2nd
: public unary_function<typename Operation::first_argument_type,
typename Operation::result_type> {
protected:
Operation op;//以要适配的仿函数为成员变量
typename Operation::second_argument_type value;//第二个参数类型
public:
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {}//传入x是对象的引用,第二参数的引用
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {//传入x,底层调用op
return op(x, value);//将value绑定为op的第二个参数
}
};
//第二个部分是方便使用的函数,以让我们可以像普通函数一样使用适配器,并通过函数模板的参数推导功能来创建适配器对象。
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {//注意这里的返回类型是binder2nd
//这有一个typedef 它提取出operation的第二参数,同时可以检测第二参数类型
typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x));//仅仅产生临时对象,可以传给模板函数
}
适配器很巧妙的构造了这样一种对象嵌套对象的结构来使得我们可以构造很复杂的语义。
举例:
// binder2nd example
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
int main () {
int numbers[] = {10,-20,-30,40,-50};
int cx;
int cx1;
binder2nd< less<int> > IsNegative (less<int>(),0);//将less<int>重新包装产生新的对象binder2nd
cx = count_if (numbers,numbers+5 , IsNegative);//二者用法一样
cx1 = count_if (numbers,numbers+5,bind2nd(less<int>() , 0));
cout << "There are " << cx <<" "<< cx1 << " negative elements.\n";//输出3,小于0的value有3个
return 0;
}
bind2nd()函数的作用: 它将0 这个变量绑定在less()函数的第二参数上,less函数返回第一参数是否小于第二参数,那么绑定后的less()函数就应该返回 传入的参数是否小于0.
相关源码分析过程如下:
1、less()的源码:
template <class T> struct less {
bool operator() (const T& x, const T& y) const {return x<y;}
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};
他也是一个仿函数,但是却发现它没有继承自binary_function<T, T, bool>,因为他自己声明了那三种typedef,他需要两个两个参数,x和y,返回 x是否小于y.
2、count_if()的源码:
template <class Inputerator, class Outputerator, class Predicate>
typename iterator_traits<Inputerator>::difference_type;
count_if(Inputerator first, Inputerator last, Predicate pred)
{
typename iterator_traits<Inputerator>::difference_type;
for( ; first != last; ++first)
if(pred(*first)) //这个地方会调用函数pred(*first), 重上面我们可以看到 pred绑定的函数是binder2nd()中的 operator()函数,那么此时的pred就应该是less函数
++n;
return n;
}
3.2.1.2、bind1st(和binder2nd()类似)
//从它继承自unary_function即可得知它也是仿函数
template <class Operation>
class binder1st
: public unary_function<typename Operation::second_argument_type,
typename Operation::result_type> {
protected:
Operation op;//以要适配的仿函数为成员变量
typename Operation::first_argument_type value;//第一个参数类型
public:
binder1st(const Operation& x,
const typename Operation::first_argument_type& y)
: op(x), value(y) {}//构造函数里对两个成员变量赋值
typename Operation::result_type
operator()(const typename Operation::second_argument_type& x) const {
return op(value, x); //重载并接受第二个参数,以完成适配
}
};
//仿函数适配器第二个部分是方便使用的函数,以让我们可以像普通函数一样使用适配器,并通过函数模板的参数推导功能来创建适配器对象
template <class Operation, class T>
inline binder1st<Operation> bind1st(const Operation& op, const T& x) {
typedef typename Operation::first_argument_type arg1_type;
return binder1st<Operation>(op, arg1_type(x));
}
3.2.1.3、bind(C++11)
namespace wzj008 {
// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}
struct MyPair {
double a,b;
double multiply() {return a*b;}
};
void test_Function_bind()
{
using namespace std::placeholders; // 使用placehloders _1(第一参数占位符), _2(第二参数占位符) ,_3(第三参数占位符)...
// binding functions:
auto fn_five = std::bind (my_divide,10,2); // returns 10/2 将10 绑定在my_divide()函数的第一参数上, 2绑定在my_divide()函数的第二参数上 10/2 = 5
std::cout << fn_five() << '\n'; // 5
auto fn_half = std::bind (my_divide,_1,2); // returns x/2 使用占位符_1 即将my_divide()函数的第一参数绑定为传入参数, 2绑定为my_divide()函数的第二参数
std::cout << fn_half(10) << '\n'; // 5 10/2 = 5
auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x 使用占位符_2 即_2为函数my_divide()的第二参数, _1为my_divide()的第一参数
std::cout << fn_invert(10,2) << '\n'; // 0.2 10(_2)为第二参数 2(_1)第一参数 2/10 = 0.2
auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y) 将fn_rounding()绑定为my_divide()函数
std::cout << fn_rounding(10,3) << '\n'; // 3
MyPair ten_two {10,2}; //绑定成员变量
// binding members:
auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
std::cout << bound_member_fn(ten_two) << '\n'; // 20 将传进参数ten_two{10,2} 的10和2 分别赋给 a和b(a = 10, b = 2) 10 * 2 = 20
auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a 给a赋值 a = 10;
std::cout << bound_member_data() << '\n'; // 10
}
}
3.2.2、not1()、not2()(函数的返回值取否)
//否定一元返回值
//模板参数传入仿函数类
template <class Predicate>
class unary_negate
: public unary_function<typename Predicate::argument_type, bool> {
protected:
Predicate pred;//对象
public:
explicit unary_negate(const Predicate& x) : pred(x) {}
bool operator()(const typename Predicate::argument_type& x) const {
return !pred(x);//这里是调用的关键
}
};
//辅助函数,使得我们方便使用unary_negate<Pred>
//传入对象,并返回临时对象。
template <class Predicate>
inline unary_negate<Predicate> not1(const Predicate& pred) {
return unary_negate<Predicate>(pred);//返回临时对象
}
//辅助函数,识别传入对象,通过模板萃取其模板型别,然后声明模板声明临时对象并用传入对象初始化。
/
//否定二元返回值
template <class Predicate>
class binary_negate
: public binary_function<typename Predicate::first_argument_type,
typename Predicate::second_argument_type,
bool> {
protected:
Predicate pred;
public:
explicit binary_negate(const Predicate& x) : pred(x) {}
bool operator()(const typename Predicate::first_argument_type& x,
const typename Predicate::second_argument_type& y) const {
return !pred(x, y);
}
};
template <class Predicate>
inline binary_negate<Predicate> not2(const Predicate& pred) {
return binary_negate<Predicate>(pred);
}
not1()举例:
#include <iostream>
#include <vector>
#include <functional>
int main(int argc, char **argv)
{
std::vector<int> nums = {5, 3, 4, 9, 1, 7, 6, 2, 8};
std::function<bool(int)> less_than_5 = [](int x){ return x <= 5; };//C++11特性std::function和lambda表达式
// 大于5的元素个数
std::cout << std::count_if(nums.begin(), nums.end(), std::not1(less_than_5)) << "\n";
return 0;
}
not2()举例
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main(int argc, char **argv)
{
std::vector<int> nums = {5, 3, 4, 9, 1, 7, 6, 2, 8};
std::function<bool(int, int)> ascendingOrder = [](int a, int b) { return a<b; };
// 递减排序
std::sort(nums.begin(), nums.end(), std::not2(ascendingOrder));
for(int i:nums) {
std::cout << i << "\t";// 输出9 8 7 6 5 4 3 2 1
}
return 0;
}
3.2.3、compose1()、compose2()(仿函数合并)
///用于函数合成/
//一元仿函数合成操作
//h(x) = f( g(x) )
template <class Operation1, class Operation2>
class unary_compose : public unary_function<typename Operation2::argument_type,
typename Operation1::result_type> {
protected:
Operation1 op1;
Operation2 op2;
public:
unary_compose(const Operation1& x, const Operation2& y) : op1(x), op2(y) {}
typename Operation1::result_type
operator()(const typename Operation2::argument_type& x) const {
return op1(op2(x));//类似f(g(x))
}
};
template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2> compose1(const Operation1& op1,
const Operation2& op2) {
return unary_compose<Operation1, Operation2>(op1, op2);//返回临时对象
}
//二元仿函数合成操作
//h(x) = f( g1(x) , g2(x) )
template <class Operation1, class Operation2, class Operation3>
class binary_compose
: public unary_function<typename Operation2::argument_type,
typename Operation1::result_type> {
protected:
Operation1 op1;
Operation2 op2;
Operation3 op3;
public:
binary_compose(const Operation1& x, const Operation2& y,
const Operation3& z) : op1(x), op2(y), op3(z) { }
typename Operation1::result_type
operator()(const typename Operation2::argument_type& x) const {
return op1(op2(x), op3(x));//返回临时对象
}
};
template <class Operation1, class Operation2, class Operation3>
inline binary_compose<Operation1, Operation2, Operation3>
compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3) {
return binary_compose<Operation1, Operation2, Operation3>(op1, op2, op3);
}
3.3、迭代器适配器(iterator adaptor)
- 迭代器大致分为 5种类型,分别是输入迭代器、输出迭代器、前向迭代器、双向迭代器以及随机访问迭代器。这 5 种迭代器是STL 标准库提供的最基础的迭代器,很多场景中遍历容器的需求,它们并不适合。
- 迭代器适配器,其本质也是一个模板类,该模板类是借助以上 5 种基础迭代器实现的。迭代器适配器模板类的内部实现,是通过对以上 5 种基础迭代器拥有的成员方法进行整合、修改,甚至为了实现某些功能还会添加一些新的成员方法。
迭代器适配器名称 | 作用 |
---|---|
反向迭代器(reverse_iterator) | 又称“逆向迭代器”,其内部重新定义了递增运算符(++)和递减运算符(–),专门用来实现对容器的逆序遍历。 |
安插型迭代器(inserter或者insert_iterator) | 通常用于在容器的任何位置添加新的元素,自动申请额外需要空间此类迭代器不能被运用到元素个数固定的容器(比如 array)上。 |
流迭代器(istream_iterator / ostream_iterator) | 输入流迭代器用于从文件或者键盘读取数据;相反,输出流迭代器用于将数据输出到文件或者屏幕上。 |
移动迭代器(move_iterator) | 此类型迭代器是 C++ 11 标准中新添加的,可以将某个范围的类对象移动到目标范围,而不需要通过拷贝去移动。 |
3.3.1、反向迭代器(reverse_iterator)
- 反向迭代器会提供与普通迭代器相反方向的遍历功能。反向迭代器其实一个正向迭代器的适配器,它的实现都是通过调用正向迭代器的操作,为了与迭代器的概念保持一致(begin指向迭代器的第一个元素,end指向迭代器的最后一个元素的下一个位置)
reverse_iterator
的实现中有一个名为current
的Iterator
,它是模板参数,即正向迭代器。- 正向迭代器指向的范围是序列中的第一个元素到最后一个元素的下一个位置,为了保持迭代器概念的统一,反向迭代器的
rbegin
应该是序列的最后一个元素,rend
应该是第一个元素前面的元素。 current
总是指向reverse_iterator
所指元素之后的一个元素。这也意味这*返回的是值*(current-1)
,++通过对current的--实现
部分源码:
template<typename _Iterator>
class reverse_iterator
: public iterator<typename iterator_traits<_Iterator>::iterator_category,
typename iterator_traits<_Iterator>::value_type,
typename iterator_traits<_Iterator>::difference_type,
typename iterator_traits<_Iterator>::pointer,
typename iterator_traits<_Iterator>::reference>
{
protected:
_Iterator current;//对应的正向迭代器
typedef iterator_traits<_Iterator> __traits_type;//迭代器萃取机
public:
typedef _Iterator iterator_type;//逆向迭代器的5中associated types 和其他迭代器相同
typedef typename __traits_type::difference_type difference_type;
typedef typename __traits_type::pointer pointer;
typedef typename __traits_type::reference reference;
//构造函数
reverse_iterator() : current() { }
explicit
reverse_iterator(iterator_type __x) : current(__x) { }
reverse_iterator(const reverse_iterator& __x)
: current(__x.current) { }
//取出对应的正向迭代器
iterator_type
base() const
{ return current; }
//关键点在这里, *操作取值,取对应正向迭代器的上一位
reference
operator*() const
{
_Iterator __tmp = current;
return *--__tmp;
}
pointer
operator->() const
{ return &(operator*()); }
//前进变后退 后退变前进
reverse_iterator&
operator++()
{
--current;
return *this;
}
reverse_iterator
operator++(int)
{
reverse_iterator __tmp = *this;
--current;
return __tmp;
}
reverse_iterator&
operator--()
{
++current;
return *this;
}
reverse_iterator
operator--(int)
{
reverse_iterator __tmp = *this;
++current;
return __tmp;
}
reverse_iterator
operator+(difference_type __n) const
{ return reverse_iterator(current - __n); }
reverse_iterator&
operator+=(difference_type __n)
{
current -= __n;
return *this;
}
reverse_iterator
operator-(difference_type __n) const
{ return reverse_iterator(current + __n); }
reverse_iterator&
operator-=(difference_type __n)
{
current += __n;
return *this;
}
reference
operator[](difference_type __n) const
{ return *(*this + __n); }
};
反向迭代器,它包含其正向迭代器,使用重载其原来的operator*,operator++等操作实现其反向功能
//反向迭代器的起点和终点函数
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
举例1:
//https://blog.csdn.net/sicofield/article/details/9047897
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> coll;
for (int i=1; i<=9; ++i)
coll.push_back(i);
vector<int>::iterator pos;
pos = find (coll.begin(), coll.end(),5);
cout << "pos: " << *pos << endl;
vector<int>::reverse_iterator rpos(pos);
cout << "rpos: " << *rpos <<endl;
}
//这个程序的输出为
//pos: 5
//rpos: 4
举例2:
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
void print(int elem)
{
cout << elem << ' ';
}
//这个程序完成反向输出功能,但是容器中元素的位置是不变的,与reverse比较
int main()
{
list<int> coll;
for(int i=1;i<=9;i++)
coll.push_back(i);
for_each(coll.begin(),coll.end(),print);
cout<<endl;
for_each(coll.rbegin(),coll.rend(),print);
cout<<endl;
return 0;
}
3.3.2、安插迭代器(inserter)
#include <iterator>
插入迭代器用于将值插入到容器中。插入迭代器是一个模板类,模板参数为容器,迭代器只需要在重载操作符函数**operator=()**中调用容器的插入操作(对应的push_back, push_front或insert)即可。
根据插入位置的不同,C++ STL 标准库提供了 3 种插入迭代器,如表 所示:
类型 | 作用 | 调用函数 |
---|---|---|
back_insert_iterator | 在指定容器的尾部插入新元素,但前提必须是提供有 push_back() 成员方法的容器(包括 vector、deque 和 list)。 | push_back (value) |
front_insert_iterator | 在指定容器的头部插入新元素,但前提必须是提供有 push_front() 成员方法的容器(包括 list、deque 和 forward_list)。 | push_front (value) |
insert_iterator | 在容器的指定位置之前插入新元素,前提是该容器必须提供有 insert() 成员方法。 | insert (pos, value) |
3.3.2.1、insert_iterator
首先看一下算法中的copy函数源码
template<class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{
while (first!=last)
{
*result = * first;
++result;
++first;
}
}
注意copy()函数中没有申请新的内存,采取的方式是直接赋值,那么当内存不够使用时,调用copy函数会报错,如下所示:
void test_Insert_Iterator()
{
int myArray[] = {10,20,30,40,50,60,70};
vector<int> v1;
v1.resize(6);//申请内存为6
copy(myArray, myArray + 7, v1.begin());//copy7个元素过来导致越界
for(auto i : v1)
{
cout << i << endl;
}
}
面这段代码是够能够编译通过;
void test_Insert_Iterator()
{
int myArray[] = {10,20,30,40,50,60,70};
vector<int> v1;
v1.resize(0);
copy(myArray, myArray + 7, inserter(v1,v1.begin()));
for(auto i : v1)
{
cout << i << endl;
}
}
功劳应该归功于运算符重载,因为insert
方法会调用到insert_iterator,
而insert_iterator
重载了operator=
,进而实现了该功能.
insert_iterator的源码如下:
template<typename _Container>
class insert_iterator
: public iterator<output_iterator_tag, void, void, void, void>
{
protected:
_Container* container;
typename _Container::iterator iter;
public:
typedef _Container container_type;
insert_iterator(_Container& __x, typename _Container::iterator __i)
: container(&__x), iter(__i) {}
…
insert_iterator&
operator=(const typename _Container::value_type& __value)
{
iter = container->insert(iter, __value);
++iter;
return *this;
}
insert_iterator&
operator=(typename _Container::value_type&& __value)//关键点在这里,它重载了operator= 使copy函数中的 *result = * first; 调用这里的operator函数
{
iter = container->insert(iter, std::move(__value));
++iter;
return *this;
}
…
}
函数调用原型:
template<typename _Container, typename _Iterator>
inline insert_iterator<_Container>
inserter(_Container& __x, _Iterator __i)
{
return insert_iterator<_Container>(__x,
typename _Container::iterator(__i));
}//这个函数使insert调用insert_iterator
应用实例:
#include <iostream>
#include <iterator>
#include <list>
using namespace std;
void op(list<int> li) {
for (int i : li) {
cout << i << ends;
}
cout << endl;
}
int main(void)
{
list<int> li;
auto b_it=back_inserter(li);
auto f_it = front_inserter(li);
auto it = inserter(li, li.begin());
for (int i = 0; i < 5; ++i) {
b_it = i;
}
cout << "back_inserter" << endl;
op(li);//输出0 1 2 3 4
for (int i = 5; i < 10; ++i) {
f_it = i;
}
cout << "front_inserter" << endl;
op(li);//输出9 8 7 6 5 0 1 2 3 4
for (int i = 10; i < 15; ++i) {
it = i;
}
cout << "inserter" << endl;
op(li);//9 8 7 6 5 0 1 2 3 4 10 11 12 13 14
return 0;
}
3.3.3、ostream_iterator
ostream_iterator
属于I/O流STL适配器,用于获取一个元素,同时保存在缓冲器中,可以供Cout输出。如果把cout
看做成一个对象,那么在Cout对象当中存在一片用于数据存储的区域。ostream_iterator在STL中一般配合copy函数一起使用。- 输入输出迭代器的思想是将输入输出流当作序列,ostream_iterator和istream_iterator相当于指向序列的迭代器,用户可以通过这个迭代器对输入输出流做操作。
- ostream_iterator的迭代器类型是
input_iterator
,只支持写操作(*p=X)
和迭代操作(++)
ostream_iterator部分源码
template<typename _Tp, typename _CharT = char,
typename _Traits = char_traits<_CharT> >
class ostream_iterator
: public iterator<output_iterator_tag, void, void, void, void>
{
…
private:
ostream_type* _M_stream;
const _CharT* _M_string;
public:
/// Construct from an ostream.
ostream_iterator(ostream_type& __s) : _M_stream(&__s), _M_string(0) {}//std::ostream_iterator<int> out_it(std::cout, “-“)将_M_stream 绑定为std::cout “-“赋值给_M_string
ostream_iterator(ostream_type& __s, const _CharT* __c)
: _M_stream(&__s), _M_string(__c) { }
/// Copy constructor.
ostream_iterator(const ostream_iterator& __obj)
: _M_stream(__obj._M_stream), _M_string(__obj._M_string) { }
/// Writes @a value to underlying ostream using operator<<. If
/// constructed with delimiter string, writes delimiter to ostream.
ostream_iterator&
operator=(const _Tp& __value)//
{
__glibcxx_requires_cond(_M_stream != 0,
_M_message(__gnu_debug::__msg_output_ostream)
._M_iterator(*this));
*_M_stream << __value;//将value传递给_M_stream 使其作出输出动作
if (_M_string) *_M_stream << _M_string;
return *this;
}
…
};
举例:
void test_ostream_iterator()
{
vector<int> v;
for(int i = 0;i<10; i++) v.push_back(i*10);
std::ostream_iterator<int> out_it(std::cout, "-");//将std::cout绑定在out_it,并且输出每个元素时加以"-"
//元素拷贝到ostream_iterator所指向的对象cout
copy(v.begin(),v.end(),out_it);
}//输出0-10-20-30-40-50-60-70-80-90
std::ostream_iterator<int> out_it(std::cout, "-")
的解释如下:
ostream_iterator<int>
指定了类型,就是迭代器读写的类型。 通过这个流迭代器可以把你要输入的写入到指定的流中。放到输出流的时候,每次放一个整数,就末尾添加一个-
.- cout就是指定的流。就是标准输出。 可以改成一个输出流就可以,比如一个文件。 你把它看成一个指向输出流的指针。通过这个指针你可以把东西写的输出流中。
copy (v.begin(),v.end(),out_it);
的解释如下:
- 把向量V中的数据放到cout输出流中,通过流迭代器
out_it.
3.3.4、istream_iterator
- 输入输出迭代器的思想是将输入输出流当作序列,ostream_iterator和istream_iterator相当于指向序列的迭代器,用户可以通过这个迭代器对输入输出流做操作。
- istream_iterator的迭代器类型是
output_iterator
,它可以支持读(=*p)
,访问(->)
,迭代(++)
,比较(==, !=)
操作。 - istream_iterator的实现跟ostream_iterator的实现大相径庭,值得注意的是istream_iterator中有一个标志标识输入序列是否结束,它的默认构造函数会将istream_iterator指向输入序列的结束位置,表示输入序列结束。当对输入迭代器作迭代操作(++)的时候,会判断序列的结束位置的值,如果该值满足,则终止读过程。
** istream_iterator部分源码**
template<class T, class charT = char, class traits = char_traits<charT>, class Distance = ptrdiff_t>
class my_istream_iterator :public iterator<input_iterator_tag, T, Distance, const T*, const T&>//其实就是继承了4个typedef,分别是iterator_category,value_type,difference_type,pointer,reference
{
basic_istream<charT, traits>*in_stream;
T value;
public:
typedef charT char_type;
typedef traits traits_type;
typedef basic_istream<charT, traits> istream_type;
my_istream_iterator() :in_stream(0) {}//constructor
my_istream_iterator(istream_type&s) :in_stream(&s) { ++*this; }//constructor,注意这里,每次构造一个对象时候,++*this,就要触发输入的动作
my_istream_iterator(const my_istream_iterator<T, charT, traits, Distance>&x)
:in_stream(x.in_stream), value(x.value) {}//copy constructor
~my_istream_iterator() {}
const T&operator*()const { return value; }
const T*operator->()const { return &value; }
my_istream_iterator<T, charT, traits, Distance>&operator++()//前置
{
if (in_stream && !(*in_stream >> value))//这里*in_stream >> value会卡在这里等待输入
in_stream = 0;
return *this;
}
my_istream_iterator<T, charT, traits, Distance> operator++(int)//后置
{
my_istream_iterator<T, charT, traits, Distance> temp = *this;
++*this;
return temp;
}
bool operator!=(my_istream_iterator<T, charT, traits, Distance>&x)
{
return this->in_stream != x.in_stream;
}
};
int main()
{
double value1, value2;
my_istream_iterator<double>eos;//end-of-stream iterator,调用无参构造函数
my_istream_iterator<double>itt(cin);//调用有参构造函数,第一次触发输入操作
if (itt != eos)
value1 = *itt;
itt++;//第二次触发输入操作
if (itt != eos)
value2 = *itt;
cout << value1 << endl;
cout << value2 << endl;
system("pause");
return 0;
}
copy函数版本如下:(同ostream_iterator)
template<class InputIterator, class OutputIterator>//函数模板
OutputIterator
my_copy(InputIterator first, InputIterator last, OutputIterator result)
{
while (first != last)
{
*result = *first;//等下会对=进行重载
++result;
++first;
}
return result;
}
template<class T, class charT = char, class traits = char_traits<charT>, class Distance = ptrdiff_t>
class my_istream_iterator :public iterator<input_iterator_tag, T, Distance, const T*, const T&>//其实就是继承了4个typedef,分别是iterator_category,value_type,difference_type,pointer,reference
{
basic_istream<charT, traits>*in_stream;
T value;
public:
typedef charT char_type;
typedef traits traits_type;
typedef basic_istream<charT, traits> istream_type;
my_istream_iterator() :in_stream(0) {}//constructor
my_istream_iterator(istream_type&s) :in_stream(&s) { ++*this; }//constructor
my_istream_iterator(const my_istream_iterator<T, charT, traits, Distance>&x)
:in_stream(x.in_stream), value(x.value) {}//copy constructor
~my_istream_iterator() {}
const T&operator*()const { return value; }
const T*operator->()const { return &value; }
my_istream_iterator<T, charT, traits, Distance>&operator++()//前置
{
if (in_stream && !(*in_stream >> value))//这里*in_stream >> value会卡在这里等待输入
in_stream = 0;
return *this;
}
my_istream_iterator<T, charT, traits, Distance> operator++(int)//后置
{
my_istream_iterator<T, charT, traits, Distance> temp = *this;
++*this;
return temp;
}
};
int main()
{
my_istream_iterator<int>eos;
my_istream_iterator<int>itt(cin);
vector<int>v;
my_copy(itt, eos, inserter(v, v.begin()));
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << " " << *it;
}
system("pause");
return 0;
}
3.3.4.1、ostream_iterator以及istream_iterator实例(包括文件的读取和输出)
实验1:
#include <iostream>
#include <numeric>
#include <algorithm>
#include <deque>
#include <iterator>
using namespace std;
int main()
{
deque<int> id;
istream_iterator<int> intie(cin),eos; //开始触发一次输入
copy(intie, eos, inserter(id, id.begin())); //迭代器类型为InputIterator,所以这里调用copy的时候采用*result = *first;版本,会使用重载类型 ,那么就会转换为插入操作
//其中++first会继续调用下一个,然后重载为新的输入
ostream_iterator<int> outie(cout, " "); //deque的迭代器类型为random_access_iterator,也会是 *result = *first;调用赋值操作 result++操作,返回本身,不影响后面的输出操作
copy(id.begin(), id.end(), outie); //将=操作,转换为输出操作
cout << endl;
system("pause");
}
特别说明:输入的时候怎样结束输入呢?win下 ctrl+z;linux下 ctrl+d
。
实验2:
从文件中读取,写到文件中去。只需将istream_iterator
绑定至ifstreamobject
,将ostream_iterator
绑定至ofstream object
即可:
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
int main()
{
ifstream in_file("input_file.txt");
ofstream out_file("output_file.txt");
if(!in_file || !out_file)
{
cout<<"filesopen failed!\n";
return -1;
}
istream_iterator<string> is(in_file);
istream_iterator<string> eof;
vector<string> text;
copy(is,eof,back_inserter(text));
sort(text.begin(),text.end());
ostream_iterator<string> os(out_file," ");
copy(text.begin(),text.end(),os);
return 0;
}
参考
1、https://ibillxia.github.io/blog/2014/11/23/insight-into-stl-7-adaptor/
2、https://www.cnblogs.com/LearningTheLoad/p/7594646.html
3、https://blog.csdn.net/u010710458/article/details/79734558
4、https://www.jianshu.com/p/c7f3a04f88b7
5、http://c.biancheng.net/view/7255.html
6、https://www.cnblogs.com/cobbliu/archive/2012/04/17/2440347.html
7、https://www.cnblogs.com/LearningTheLoad/p/7639042.html
2、《STL源码剖析》