迭代器
标准库容器类型上所有迭代器都允许我们访问容器中的元素,下面的表中列出了容器迭代器支持的所有操作,其中有一个例外:forward_list迭代器不支持递减运算符。
迭代器操作 | 说明 |
---|---|
*iter | 返回迭代器所指元素的引用 |
iter->member | 解引用iter并获取该元素的名为member的成员,等价于(*iter).member |
++iter | 令iter指向容器中下一个元素 |
--iter | 令iter指向容器中上一个元素 |
iter1 == iter2 | 判断两个迭代器是否指向的是同一个元素或者是同一个容器的尾后迭代器 |
iter1 != iter2 | 同上 |
容器中的begin和end迭代器指示了容器中的元素范围,其中end迭代器不指向最后一个元素,而是指向尾元素之后的位置,这种元素范围被称为左闭合区间:[begin,end),假设begin和end构成了一个合法的迭代器范围,则:
- 如果begin与end相等,则范围为空。
- 如果begin与end不等,则范围至少包含一个元素,且begin指向该范围中的第一个元素。
- 我们可以对begin递增若干次使得begin==end。
begin和end有多个版本,其中带r的版本返回反向迭代器,c开头的版本返回const迭代器。
使用迭代器遍历数组:
int main()
{
int values1[] = { 1,2,3,4,5 };
for (auto it = begin(values1); it != end(values1); it++) {
cout << *it << endl;
}
system("pause");
}
插入迭代器
插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素,当我们给一个插入迭代器赋值时,迭代器调用容器操作来向给定容器的指定位置插入一个元素,插入完成后迭代器保持不变。*it,++it,it++不会对插入迭代器做任何事情,每个操作都返回it。
插入迭代器有三种类型:
- back_inserter,创建一个使用push_back的迭代器。
- front_inserter,创建一个使用push_front的迭代器。
- inserter创建一个使用insert的迭代器,此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器,元素将被插入到给定迭代器所表示的元素之前。
int main()
{
vector values{ 1,2,3,4 };
list list1, list2,list3;
copy(values.cbegin(), values.cend(), back_inserter(list1));
copy(values.cbegin(), values.cend(), front_inserter(list2));
copy(values.cbegin(), values.cend(), inserter(list3,list3.begin()));
for (const auto& e : list1) {
cout << e << endl;//1 2 3 4
}
for (const auto& e : list2) {
cout << e << endl;//4 3 2 1
}
for (const auto& e : list3) {
cout << e << endl;//1 2 3 4
}
system("pause");
}
iostream迭代器
虽然iostream不是容器,但标准库定义了可以用于这些IO类型对象的迭代器,istream_iterator读取输入流,ostream_iterator向一个输出流写数据,这些迭代器将它们对应的流当作一个特定类型的元素序列来处理,通过使用流迭代器,我们可以用泛型算法从流对象读取数据或写入数据。
创建一个流迭代器时,必须指定迭代器将要读写的对象类型,还可以将流迭代器绑定到一个流,如果不绑定它将是一个可以当作尾后值使用的流迭代器。一旦关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相等。
int main()
{
vector values;
istream_iterator inIter(cin);
istream_iterator eof;
while (inIter != eof) {
//先调用后置递增运算符,然后对迭代器的旧值进行解引用
values.push_back(*inIter++);
}
for (const auto& e : values) {
cout<
我们可以将上面的代码重写为如下形式,values构造时从cin读取数据,直到遇到文件尾或者不是int的数据为止。
int main()
{
istream_iterator inIter(cin), eof;
vector values(inIter, eof);
for (const auto& e : values) {
cout<
由于算法使用迭代器操作来处理数据,而流迭代器又至少支持某些迭代器操作,因此我们可以使用某些算法来操作流迭代器,下面的代码计算从输入流读取的值的和。
int main()
{
istream_iterator inIter(cin), eof;
cout << accumulate(inIter, eof, 0) << endl;
system("pause");
}
当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据,具体实现可以推迟从流中读取数据,标准库保证的是在我们解引用迭代器之前,从流中读取的操作已经完成了。
int main()
{
vector values;
istream_iterator inIter(cin);
//输入一个值后才会显示ready now
cout << "ready now" << endl;
values.push_back(*inIter);
system("pause");
}
当创建一个ostream_iterator时,我们可以提供(可选的)第二个参数,它是一个C风格字符串,在输出每个元素后都会打印此字符串。
ostream_iterator必须绑定到一个流,*out,++out,out++这些运算符虽然存在但不对out做任何事情,每个运算符都返回out。
对ostream_iterator使用赋值运算符(=)时,会用<<运算符将等号右边的值写入到所绑定的流中,该值的类型必须与流可写的类型兼容。
int main()
{
ostream_iterator outIter(cout," ");
vector values{1.0,2.0,3.0};
for (const auto& e : values) {
outIter = e;//1 2 3
}
system("pause");
}
还可以通过copy打印vector中的元素,这比上面的代码更简单。
int main()
{
ostream_iterator outIter(cout," ");
vector values{1,2,3};
copy(values.cbegin(), values.cend(), outIter);//1 2 3
system("pause");
}
反向迭代器
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器,对于反向迭代器,递增或递减的操作含义会颠倒过来。通过成员函数base可以获得反向迭代器对应的普通迭代器。
int main()
{
vector values1 = { 10,2,8,0,9,1 };
//按逆序排序
sort(values1.rbegin(),values1.rend());
ostream_iterator outIter(cout," ");
copy(values1.cbegin(), values1.cend(), outIter);//10 9 8 2 1 0
//反向输出
copy(values1.crbegin(), values1.crend(), outIter);//0 1 2 8 9 10
//通过base()正向输出
copy(values1.crend().base(), values1.crbegin().base(), outIter);//10 9 8 2 1 0
system("pause");
}
移动迭代器
移动迭代器通过改变给定迭代器的解引用运算符的行为来适配此迭代器,普通迭代器的解引用运算符返回一个指向元素的左值,而移动迭代器解引用运算符生成一个右值引用。我们通过调用make_move_iterator将普通迭代器转换为一个移动迭代器。
由于移动一个对象可能销毁原对象,因此只有在确信算法或自定义的函数使用完元素后不再访问它时,才能将移动迭代器传递过去。
在库代码中使用移动操作(std::move,make_move_iterator)能提升性能,但是如果在用户代码中随意使用移动操作,很可能会导致莫名其妙,难以查找的错误。