迭代器
迭代器是一个”可遍历STL容器内全部或部分元素“,一个迭代器用来指出容器中的一个特定位置。
- operator * 返回当前位置上的元素值。如果该元素拥有成员,你可以通过迭代器直接使用operator->取用它们;
- operator++ 将迭代器前进至下一元素,大多数迭代器还可使用operator--退回到前一个元素;
- operator==和operator!= 判断两个迭代器是否指向同一位置;
- operator= 为迭代器赋值;
所有容器类别都提供一些成员函数,使我们得以获得迭代器并以之访问所有元素,其中最重要的两个函数是
begin()返回一个迭代器,指向容器起始点,也就是第一元素的位置;
end() 返回一个迭代器,指向容器最后一个元素的下一个位置;
下面这个例子展示了迭代器的用法:
#include<iostream>
#include<list>
using namespace std;
int main()
{
list<char> col;
for(char c='a';c<='z';c++)
{
col.push_back(c);
}
list<char>::const_iterator it;
for(it=col.begin();it!=col.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
}
迭代器有两种类型
一种是container::iterator 以”读/写“模式遍历元素
一种是container::const_iterator 以”只读“模式遍历元素
当然我们也可以使用非常量迭代器,这时迭代器是非常量类型的、元素类型也是非常量类型的,我们就可以对容器中的元素进行修改
这里我们将上一个程序的输出全部变为大写
#include<iostream>
#include<list>
using namespace std;
int main()
{
list<char> col;
for(char c='a';c<='z';c++)
{
col.push_back(c);
}
list<char>::iterator it;
for(it=col.begin();it!=col.end();++it)
{
cout<<char(toupper(*it))<<" ";
}
cout<<endl;
}
这里就是输出结果,使用了非常量迭代器在输出前对每一个元素进行操作,
迭代器类型 | 能力 | 供应者 |
Input迭代器 | 向前读取(read) | istream |
Output迭代器 | 向前写入(write) | ostream,inserter |
Forward迭代器 | 向前读取和写入 | |
Bidirectional迭代器 | 向前和向后读取和写入 | list,set,multiset,map,multimap |
Random access迭代器 | 随机存取,可读取也可写入 | vector,deque,string,array |
-
Input(输入)迭代器
Input迭代器只能一次一个向前读取元素 ,按此顺序一个个传回元素值,下表列出了Input迭代器的各种操作行为
表达式 | 效果 |
*iter | 读取实际元素 |
it->member | 读取实际元素的成员 |
++iter | 向前步进(传回新位置) |
iter++ | 向前步进(传回旧位置) |
iter1==iter2 | 判断两个迭代器是否相等 |
iter1!=iter2 | 判断两个迭代器是否不相等 |
TYPE(iter) | 复制迭代器 |
应该尽可能地优先使用前置递增运算符(++iter),因为它不需要传回旧值,所以不需要新建一个临时对象来保存旧值
-
Output(输出)迭代器
Output迭代器和Input迭代器相反,其作用是将元素值一个个写入。也就是说,只能一个元素一个元素地赋值,下表列出Output迭代器的有效操作
表达式 | 效果 |
*iter=value | 将数值写入到迭代器所指位置 |
++iter | 向前步进(传回新位置) |
iter++ | 向前步进(传回旧位置) |
TYPE(iter) | 复制迭代器 |
-
Forward(前向)迭代器
Forward迭代器是Input迭代器和Output迭代器的结合,具有Input迭代器全部功能和Output迭代器的大部分功能,下表是Forward迭代器的所有操作
表达式 | 效果 |
*iter | 存取实际元素 |
iter->member | 存取实际元素的成员 |
++iter | 向前步进(返回新位置) |
iter++ | 向前步进(返回旧位置) |
iter1==iter2 | 判断两个迭代器是否相等 |
iter1!=iter2 | 判断两个迭代器是否不想等 |
TYPE() | 产生迭代器(default构造函数) |
TYPE(iter) | 复制迭代器(copy构造函数) |
iter1=iter2 | 赋值 |
-
Bidirectional(双向)迭代器
在Forward迭代器的基础上增加了反向遍历的能力
支持Forward迭代器的所有操作 | |
--iter | 步退(返回新位置) |
iter-- | 步退(返回旧位置) |
-
Random Access(随机存取迭代器)
Random Access迭代器在Bidirectional迭代器的基础上再增加随机存取能力。因此它必须提供“迭代器算术运算”,也就是说它能加减某个偏移量、能处理距离问题,并运用关系运算符进行比较
支持Bidirectional迭代器的所有操作 | |
iter[n] | 存取索引位置为n的元素 |
iter+=n | 向前跳n个元素(n<0,就向后跳) |
iter-=n | 向后跳n个元素(n<0,就向前跳) |
iter+n | 传回iter之后的第n个元素 |
n+iter | 传回iter之后的第n个元素 |
iter-n | 传回iter之前的第n个元素 |
iter1-iter2 | 传回iter1和iter2之间的距离 |
iter1<iter2 | 判断iter1与iter2的前后关系 |
iter1>iter2 | 判断iter1与iter2的前后关系 |
iter1<=iter2 | 判断iter1与iter2的前后关系 |
iter1>=iter2 | 判断iter1与iter2的前后关系 |
用以下程序说明该迭代器的特殊能力
#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> col;
//insert slements from -3 to 9
for (int i = -3; i <= 9; ++i)
{
col.push_back(i);
}
cout << "numbers distance: " << col.end() - col.begin() << endl;
//print all elements use < instead of !=
vector<int>::iterator it;
for (it = col.begin(); it <col.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//print all elements use operator []
for (int i = 0; i < col.size(); ++i)
{
cout << col.begin()[i] << " ";
}
cout << endl;
//print odd position elements use operator+=
for (it = col.begin(); it < col.end()-1; it += 2)
{
cout << *it << " ";
}
}
注意程序的最后一个循环,it<col.end()-1;这里要求col至少包含一个元素。如果序列为空,col.end()-1便会位于col.begin()之前;
-
迭代器相关辅助函数
C++标准库为迭代器提供了三个辅助函数:advance(),distance()和iter_swap()。前两者提供给所有迭代器一些原本只有Random Access迭代器才有的能力:前进或后退多个元素,及处理迭代器之间的距离,第三个辅助函数允许交换两个迭代器的值。
-
advance()可令迭代器前进
advance()可将迭代器的位置增加,增加的幅度由参数决定
#include<iterator>
void advance(InputIterator &pos,Dist n)
- 使名为pos的Input迭代器步进或(步退)n个元素
- 对Bidirectional迭代器和Random Access迭代器而言,n可为负值,表示向后退
- Dist 是个template型别。通常应该是个整型
- advance()并不检查迭代器是否超过序列的end()(因为迭代器通常不知道其操作的容器,因此无从检查)。所以调用advance()有可能导致未定义行为
下面是advance()运用实例
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
int main()
{
list<int> col;
//insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
list<int>::iterator it = col.begin();
//print actual element
cout << *it << endl;
//step 3 elementd forward
advance(it, 3);
//print actual element
cout << *it << endl;
//step 1 element backward
advance(it, -1);
//print actual element
cout << *it << endl;
}
在这个程序中,advance()使迭代器向前进3个元素,再向后退一个元素。
-
distance()可处理迭代器之间的距离
函数distance()用来处理两个迭代器之间的距离
#include<iterator>
Dist distance(InputIterator pos1,InputIterator pos2)
- 传回两个Input迭代器pos1和pos2之间的距离
- 两个迭代器都必须指向同一个容器
- 如果不是Random Access迭代器,则从pos1向前走必须能够到达pos2
- 返回值Dist的类型由迭代器决定
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
int main()
{
list<int> col;
//insert elements
for (int i = -3; i <= 9; ++i)
{
col.push_back(i);
}
cout << " col: ";
for (auto c : col)
{
cout << c << " ";
}
cout << endl;
//search element with value 5
list<int>::iterator pos;
pos = find(col.begin(), col.end(), 5);
if (pos != col.end())
{
cout << "difference between beginning and 5: "
<< distance(col.begin(), pos) << endl;
}
else
{
cout << "5 not found" << endl;
}
}
find()将数值为5的元素的位置赋给pos。distance()计算出pos和起点位置间的距离
-
iter_swap()可交换两个迭代器所指内容
#include<algorithm>
void iter_swap(ForwardIterator1 pos1,ForwardIterator2 pos2)
- 交换迭代器pos1和pos2所指的值
- 迭代器的类型不必相同,但其所指的两个值必须可以相互赋值
#include"algostuff.h"
using namespace std;
int main()
{
list<int> col;
//insert elements form 1 to 9
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
PRINT_ELEMENTS(col,"col: ");
//swap first and second value
iter_swap(col.begin(), ++col.begin());
PRINT_ELEMENTS(col, "after swap col: ");
//swap first and last value
iter_swap(col.begin(), --col.end());
PRINT_ELEMENTS(col, "after swap col: ");
}
程序中的algostuff.h
-
迭代器配接器
这些特殊迭代器使得算法可以以逆向模式(reverse mode)、安插模式(insert mode)进行工作,也可以和流(stream)搭配工作
- Reverse(逆向)迭代器
Reverse迭代器是一种配接器,重新定义递增运算和递减运算,使其行为正好倒置。所有标准容器都允许使用Reverse迭代器来遍历元素
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
void print(int elem)
{
cout << elem << " ";
}
int main()
{
list<int> col;
//insert element form 1 to 9
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
//print all elements
for_each(col.begin(), col.end(), print);
cout << endl;
//print all elements in reversed order
for_each(col.rbegin(), col.rend(), print);
cout << endl;
}
容器的成员函数rbegin()和rend()各传回一个Reverse迭代器,它们就像begin()和end()的返回值一样,共同定义出一个半开区间,不同的是它们以相反的方向操作
rbegin()传回逆向遍历的第一元素位置,也就是实际上最后一个元素的位置;
rend()传回逆向遍历时最后一个元素的下一个位置,也就是实际第一元素的前一个位置;
可以将一般迭代器转换成一个Reverse迭代器,前提是那个迭代器必须具有双向移动能力。注意,转换前后迭代器的逻辑位置发生了变化。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
vector<int> col;
//insert element form 1 to 9
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
//find position of element with value 5
vector<int>::iterator pos;
pos=find(col.begin(), col.end(), 5);
cout << "pos: " << *pos << endl;
//convert iterator to reverse iterator rpos
vector<int>::reverse_iterator rpos(pos);
//rpos = find(col.rbegin(), col.rend(), 5);
cout << "rpos: " << *rpos << endl;
}
我们首先打印迭代器所指的元素,然后将该迭代器转化成一个Reverse迭代器,再次打印它所指的元素,结果其值竟然变了,从5变成了4。注意这是特性而不是错误!导致这个行为的原因是区间的半开性,为了能够指定容器内的所有元素,必须使用“最后一个元素的下一位置”。而对Reverse迭代器而言,这个位置位于第一元素之前,但是这个位置有可能并不存在,因为容器并不要求其第一元素之前的位置合法。为此,逆向迭代器实际上倒置“半开原则”,逆向迭代器所定义的区间,实际上并不包括起点,反倒是包括了终点,然而逻辑上其行为与正向迭代器相同,这么一来,逆向迭代器实际所指向的元素位置和逻辑上所指的元素位置并不一致。
下面我们看这一个例子
#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;
void print(int elem)
{
cout << elem << " ";
}
int main()
{
deque<int> col;
//insert element form 1 to 9
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
//find position of element with value 2
deque<int>::iterator pos1;
pos1=find(col.begin(), col.end(), 2);
//find position of element with value 7
deque<int>::iterator pos2;
pos2 = find(col.begin(), col.end(), 7);
//print all element in range[pos1,pos2)
for_each(pos1, pos2, print);
cout << endl;
//convert iterator to reverse iterator
deque<int>::reverse_iterator rpos1(pos1);
deque<int>::reverse_iterator rpos2(pos2);
//print all element in range [pos1,pos2) in reverse order
for_each(rpos2, rpos1, print);
cout << endl;
}
迭代器pos1和pos2定义一个半开区间,包括2而不包括7。当把这两个迭代器转化成逆向迭代器时,该区间仍有效,而且可以被逆序处理。
当然也可以用base()将逆向迭代器转回正常迭代器
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
int main()
{
list<int>col;
//insert element from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
//find position of value 5
list<int>::iterator pos;
pos=find(col.begin(), col.end(), 5);
cout << "pos: " << *pos << endl;
//convert iterator to reverse iterator
list<int>::reverse_iterator rpos(pos);
//print value at rpos
cout << "rpos: " << *rpos << endl;
//convert reverse iterator to normal iterator
list<int>::iterator rrpos;
rrpos = rpos.base();
cout << "rrpos: " << *rrpos << endl;
}
-
Insert(安插型)迭代器
Insert迭代器用来将“赋值新值”操作转换为“安插新值”操作。通过这种迭代器,算法可以执行安插操作而非覆盖行为
insert迭代器的种类
C++ 标准器提供三种Insert迭代器,back Insert,front Insert,general Insert。它们之间的区别在于插入位置,事实上它们各自调用所属容器中不同的成员函数,所以Insert迭代器初始化时一定要清楚知道自己所属的容器是哪一种。每一种Insert迭代器都可以由一个对应的便捷函数加以生成和初始化。
名称 | Class | 其所调用的函数 | 生成函数 |
back insert | back_insert_iterator | push_back(value) | back_insert(cont) |
front insert | front_insert_iterator | push_front(value) | front_insert(cont) |
general insert | insert_iterator | insert(pos,value) | inserter(cont,pos) |
当然容器本身必须支持Insert迭代器所调用的函数,否则该种Insert就不可用。因此back insert 只能用在vector、deque、list、string身上,front insert只能用在deque 和list身上。
back Insert透过成员函数push_back()将一个元素值追加于容器尾部,它生成时必须根据其所属容器进行初始化。
#include"algostuff.h"
using namespace std;
int main()
{
vector<int> col;
back_insert_iterator<vector<int>>iter(col);
//insert elemnets with the usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
iter++;
PRINT_ELEMENTS(col, "col: ");
//create back inserter and insert elements
back_inserter(col) = 44;
back_inserter(col) = 55;
PRINT_ELEMENTS(col, "col: ");
//use back inserter to append all elements again
col.reserve(2 * col.size());
copy(col.begin(), col.end(), back_inserter(col));
PRINT_ELEMENTS(col, "col: ");
}
front Insert透过成员函数push_front()将一个元素值加在容器头部。由于push_front()只在deque和list中实现,所以也只有这两个容器支持front Insert。
#include"algostuff.h"
using namespace std;
int main()
{
list<int> col;
front_insert_iterator<list<int>>iter(col);
//insert elements with the usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
PRINT_ELEMENTS(col, "col: ");
//create dront inserter and insert elements
front_inserter(col) = 44;
front_inserter(col) = 55;
PRINT_ELEMENTS(col, "col: ");
//user front inserter to insert all elements again
copy(col.begin(), col.end(), front_inserter(col));
PRINT_ELEMENTS(col, "col: ");
}
安插多个元素时,front inserter以逆序方式插入,这是因为它总是将后一个元素插入到前一个元素之前
- General Inserter
General Inserter根据两个参数初始化:(1)容器(2)待安插位置。迭代器内部以“待安插位置”为参数,调用成员函数insert()。便捷函数inserter()则是提供了更为方便的方式
General Inserter对所有标准容器均适用,因为所有容器都有insert()成员函数。按插完成后general inserter获得刚刚被安插的那个元素的位置。
#include"algostuff.h"
using namespace std;
int main()
{
set<int> col;
//create insert iterator for col
insert_iterator<set<int>>iter(col, col.begin());
//insert elements with usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
PRINT_ELEMENTS(col, "col: ");
//create inserter and insert elements
inserter(col, col.end())=44;
inserter(col, col.end()) = 55;
PRINT_ELEMENTS(col, "col: ");
//use inserter to insert all elements into a list
list<int>col2;
copy(col.begin(), col.end(), inserter(col2, col2.begin()));
PRINT_ELEMENTS(col2, "col2(list): ");
//use inserter to reinsert all elements into the list before the second element
copy(col.begin(), col.end(), inserter(col2, ++col2.begin()));
PRINT_ELEMENTS(col2, "col2: ");
}
-
Stream(流)迭代器
Stream迭代器是一种迭代器配接器,通过它可以把stream当作算法的起点和终点
- ostream迭代器
ostream迭代器可以将被赋予的值写入output stream中,它的实作机制和Insert迭代器概念一样,唯一的区别是ostream迭代器将赋值操作转化为operator<<。如此一来算法就可以使用一般的迭代器接口直接对stream执行动作。
ostream_iterator<T>(ostream) | 为ostream产生一个ostream迭代器 |
ostream_iterator<T>(ostream,delim) | 为ostream产生一个ostream迭代器,各元素之间以delim为分隔符(delim的类型为const char*) |
*iter | 无实际操作(传回iter) |
iter=value | 将value写到ostream |
++iter | 无实际操作(传回iter) |
iter++ | 无实际操作(传回iter) |
产生ostream迭代器时,必须提供一个output stream作为参数,迭代器将会把元素值写到该output stream身上,另外一个作为分隔符的参数可有可无
#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
using namespace std;
int main()
{
//create ostream iterator for stream cout
ostream_iterator<int> intWriter(cout, "\n");
//write elements with th uaual iterator interface
*intWriter = 42;
intWriter++;
*intWriter = 77;
intWriter++;
*intWriter = -5;
//create collection with elements from 1 to 9
vector<int>col;
for (int i = 1; i <= 9; ++i)
{
col.push_back(i);
}
//write all elements without any delimiter
copy(col.begin(), col.end(), ostream_iterator<int>(cout));
cout << endl;
//write all elements with "<" as delimiter
copy(col.begin(), col.end(), ostream_iterator<int>(cout, " < "));
cout << endl;
}
分隔符的类型是const char*,如果传递一个string对象进去,需调用c_str()成员函数获得正确类型
string delim;
ostream_iterator<int>(cout, delim.c_str());