在上一篇文章https://blog.csdn.net/Master_Cui/article/details/108512730谈到的迭代器是基本的五种类型的迭代器
但是随着C++标准库的扩展,又实现了一些迭代器,这些迭代器的操作和之前的五中操作类似,所以成为迭代器适配器,类似容器适配器
插入迭代器分为三种:back inserter,front insertter和inserter
template <class Container>
class back_insert_iterator;
template <class Container>
back_insert_iterator<Container> back_inserter (Container& x);
explicit back_insert_iterator (Container& x) : container(&x) {}//back_insert_iterator的构造函数
其中back_insert_iterator是back inserter的类名,back_inserter是back_insert_iterator的生成函数,返回back_insert_iterator的对象,该插入迭代器指向被插入的元素
该迭代器向容器的末尾插入新的元素,内部实现调用了push_back,所以,back inserter插入元素的容器必须支持push_back操作,所以,只有vector,list,deque和string支持back inserter
示例
void backinsertertest()
{
list<int> l;
back_insert_iterator<list<int>> lit(l);
*lit=1;
++lit;
*lit=2;
++lit;
for (list<int>::iterator it=l.begin();it!=l.end();++it) {
cout<<*it<<endl;
}
cout<<"---------------"<<endl;
back_inserter(l)=3;
back_inserter(l)=4;
for (list<int>::iterator it=l.begin();it!=l.end();++it) {
cout<<*it<<endl;
}
cout<<"---------------"<<endl;
list<int> l2;
copy(l.begin(), l.end(), back_inserter(l2));
cout<<"---------------"<<endl;
for (list<int>::iterator it=l2.begin();it!=l2.end();++it) {
cout<<*it<<endl;
}
}
注意:list虽然支持push_back,但是copy时,back_inserter中的参数不能是原来的容器,必须得是另外一个list对象
当把上面的第20行代码替换为copy(l.begin(), l.end(), back_inserter(l));时,程序会卡在第20行,原因暂时未知!!!!
如果不在copy中使用back_inserter,那么插入的容器要事先分配元素数量,因为copy不负责分配内存
具体使用方法见博客https://blog.csdn.net/Master_Cui/article/details/108404257
2.front inserter的相关声明
template <class Container>
class front_insert_iterator;
template <class Container>
front_insert_iterator<Container> front_inserter (Container& x);
explicit front_insert_iterator (Container& x) : container(&x) {}
意义参考back inserter
该迭代器向容器的头部插入新的元素,内部实现调用了push_front,所以,front inserter插入元素的容器必须支持push_front操作,所以,list和deque支持back inserter
示例
void frontinsertertest()
{
list<int> l;
front_insert_iterator<list<int>> lit(l);
*lit=1;
++lit;
*lit=2;
++lit;
for (list<int>::iterator it=l.begin();it!=l.end();++it) {
cout<<*it<<endl;
}
cout<<"---------------"<<endl;
front_inserter(l)=3;
front_inserter(l)=4;
for (list<int>::iterator it=l.begin();it!=l.end();++it) {
cout<<*it<<endl;
}
cout<<"---------------"<<endl;
copy(l.begin(), l.end(), front_inserter(l));
cout<<"---------------"<<endl;
for (list<int>::iterator it=l.begin();it!=l.end();++it) {
cout<<*it<<endl;
}
}
由于push_front会把元素插入到容器的头部,所以,容器里的数据是反序的
在拷贝时将4321逐个拷贝到容器的头部,所以最终结果是12344321
非常的怪异,front_insert就没有上面卡住的问题,C++标准库真是个神奇的东西
3.inserter的相关声明
template <class Container>
class insert_iterator;
template <class Container>
insert_iterator<Container> inserter (Container& x, typename Container::iterator it);
explicit insert_iterator (Container& x, typename Container::iterator i) : container(&x), iter(i) {}
意义和back inserter类似
该迭代器向容器中的指定位置插入元素,内部调用的是成员函数insert(pos, val)
示例
void insertertest()
{
vector<int> v;
insert_iterator<vector<int>> it(v,v.begin());//创建一个插入迭代器对象,并指定插入的容器以及插入的位置
*it=1;
++it;
*it=2;
++it;
for (vector<int>::iterator it=v.begin();it!=v.end();++it) {
cout<<*it<<endl;
}
cout<<"---------------"<<endl;
inserter(v, v.begin())=3;
inserter(v, v.begin())=4;//分别在头部插入3和4
for (vector<int>::iterator it=v.begin();it!=v.end();++it) {
cout<<*it<<endl;
}
cout<<"---------------"<<endl;
cout<<v.capacity()<<endl;
v.reserve(v.size()*2);
copy(v.begin(),v.end(),inserter(v, v.end()));
for (vector<int>::iterator it=v.begin();it!=v.end();++it) {
cout<<*it<<endl;
}
}
因为copy不负责扩大容器的空间,所以上述代码中,当要使用copy重新向vector中添加元素时,需要扩大vector对象所能容纳元素的个数,否则会产生迭代器失效的问题
copy之前,vector在不重新分配内存的情况下最多能容纳四个元素,所以要想容纳八个元素,需要调用reserve方法重新分配内存
关于vector的增长与迭代器失效见博客https://blog.csdn.net/Master_Cui/article/details/107503634
二、流迭代器
常用的流迭代器有两个:istream_iterator(输入流迭代器)、ostream_iterator(输出流迭代器),这两个迭代器一般和cin与cout一起使用
1.istream_iterator以及相关操作
其中,T是流中数据的类型
示例
void istream_iteratortest()
{
istream_iterator<int> cinit(cin);
istream_iterator<int> eofit;//eof迭代器对象
while(cinit != eofit) {
cout<<*cinit<<endl;//将输入迭代器的数据解引用并写入输出流
++cinit;//读取下一个输入数据
}
}
通过输入流迭代器,可以像操作其他常用对象一样操作输入流
2.ostream_iterator以及相关操作
其中,T是写入流数据的类型
示例
void ostream_iteratortest()
{
ostream_iterator<int> coutit(cout, ",");
*coutit=1;
++coutit;
*coutit=2;
++coutit;
deque<int> dq={3,4,5,6,7};
copy(dq.begin(),dq.end(),coutit);
cout<<endl;
}
上面的代码通过copy将deque中的数据写入到输出流,然后依次打印出来,一行实现了循环打印的功能
三、反向迭代器
反向迭代器重新实现了递增递减操作,使其和一般迭代器的行为正好相反,一般用来反向操作容器中的元素
示例
void reverseiteratortest()
{
vector<int> v={1,2,3,4,5,6,7,8,9};
for (vector<int>::reverse_iterator it=v.rbegin();
it!=v.rend();++it) {
cout<<*it<<endl;
}
}
上述代码实现了反序打印容器的功能,使用方法begin与end相同
C++标准库中使用rbegin()和rend()来获取反向迭代器,其中,rbegin()指向的元素是最后一个元素,迭代器位置最后一个元素的后一个位置,rend()指向的是首元素的前一个元素(不确定的值),迭代器位置首元素的位置
也就是说,反向迭代器的实际位置和值所在的位置是错开的,用下图来表示
上面这种反人类的特性就造成了下面的这个bug,
示例
void reverseiteratortest2()
{
vector<int> v={1,2,3,4,5,6,7,8,9};
vector<int>::iterator pos=find(v.begin(),v.end(),5);
if (pos!=v.end()) {
cout<<*pos<<endl;
}
vector<int>::reverse_iterator rpos(pos);//注意,不能使用等号进行初始化
if (rpos!=v.rend()) {
cout<<*rpos<<endl;
}
}
正因为反向迭代器的这个bug,所以相同位置的迭代器解引用出来的值确实不一样的
用图来表示就是下面这样的
如何避免这个bug呢,那就是使用一般迭代器,如果代码里有反向迭代器,通过base函数将其转化为一般迭代器
代码如下
void reverseiteratortest2()
{
vector<int> v={1,2,3,4,5,6,7,8,9};
vector<int>::iterator pos=find(v.begin(),v.end(),5);
if (pos!=v.end()) {
cout<<*pos<<endl;
}
vector<int>::reverse_iterator rpos(pos);
if (rpos!=v.rend()) {
cout<<*rpos.base()<<endl;//这里不同,调用反向迭代器的base方法将反向迭代器转化为一般迭代器
}
}
所以,不要混用反向迭代器和一般迭代器,在使用迭代器的时候,优先使用一般迭代器,尽量不使用反向迭代器,如果使用了,在解引用的时候,最好通过base成员函数将反向迭代器转化为一般迭代器,真不明白C++的反向迭代器为啥会设计的这么脑抽
参考
《C++ Primer》
《C++标准库》
http://www.cplusplus.com/reference/iterator/
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出