8、泛型程序设计与c++标准模板库3.迭代器
理解迭代器对于理解STL框架并掌握STL的使用至关重要。简单地说,迭代器是面向对象版本的指针,STL算法利用迭代器对存储在容器中的元素序列进行遍历,迭代器提供了访问容器和序列中每个元素的方法。
虽然指针也是一种迭代器,但迭代器却不仅仅是指针。指针可以指向内存中的一个地址,通过这个地址就可以访问相应的地址。而迭代器更为抽象,它可以指向容器中的一个位置,我们也许不必关心这个位置的真正物理地址,但是我们可以通过迭代器访问这个位置的元素。
迭代器是算法和容器的“中间人”,遍历链表需要指针,对数组元素进行排序要通过下标来访问数组元素。那么,指针和下标运算符便充当了算法和数据结构的“中间人”;在STL中,容器是封装起来的类模板,其内部结构无从知晓,我们只能通过容器接口来使用容器,但是STL中的算法是通用的函数模板,并不专门针对那一个容器类型。这时请想一想:算法要适用于多种容器,而每一种容器存放的元素又可以是任何类型,如何用普通的指针或下标类充当中介呢?使用指针需要直到其指向的元素类型,使用下标需要在相应的容器中定义过下标操作符,但不是每个容器中都有下标操作符的。这时就必须使用更为抽象的指针--迭代器。
就像我们声明指针时要说明其指向的元素一样,STL的每一个容器类模板中,都定义了一组对应的迭代器类。使用迭代器,算法函数可以访问容器中指定位置的元素,而无需关心元素的具体类型。
1、迭代器的类型
为了满足某些特定算法的需要,STL迭代器主要包括5种基本迭代器类别:输入、输出、前向、双向和随机访问,以及两种迭代器适配器:逆向迭代器适配器和插入迭代器适配器。
1)迭代器分类
输入迭代器对象可以用来从序列中读取数据,但是不一定能够向其中写入数据。输出迭代器具有刚好相反的功能,它允许向序列中写入数据,但是并不保证可以从其中读取数据。
前向迭代器既是输入迭代器又是输出迭代器,因此它即支持数据读取,也支持数据写入,并且可以对序列进行单向的遍历。
双向迭代器的功能与前向迭代器相似,不同之处在于,双向迭代器在两个方向上都可以对数据遍历。也就是说,双向迭代器必须支持前向迭代器的所有操作,此外,还必须支持双向操作,从而使对序列进行反向遍历。
随机访问迭代器也是双向迭代器,它对迭代器提供了更高的要求,能够在序列中的任意两个位置之间进行跳转。
2、迭代器适配器
适配器是用来修饰或调整其他类接口的,迭代器适配器便是用来扩展(或调整)迭代器功能的类。当然这样的适配器本身也称为迭代器,只是这种迭代器是通过改变另一个迭代器而得到的。STL中定义了两类迭代器适配器:
逆向迭代器是一种适配器,它通过重新定义递增运算和递减运算,使其行为正好倒置。这样,使用这类迭代器,算法将以逆向次序处理元素。所有标准容器都允许使用逆向迭代器来遍历元素。
插入型迭代器用来将赋值操作转换为插入操作。通过这种迭代器,算法可以执行插入行为而不是覆盖行为。c++标准程序库提供了3种插入型迭代器:后插入型迭代器、前插入型迭代器、普通插入型迭代器。
例子:
应用逆向迭代器和后插迭代器来操作向量容器中的元素
#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
using namespace std;
int main()
{
int A[] = {1,2,3,4,5};
const int N = sizeof(A) / sizeof(int);
vector<int> col1(A,A+N);
ostream_iterator<int>output(cout," ");
cout << "List col1 contains:";
copy(col1.begin(),col1.end(),output);
vector<int>::iterator pos = col1.begin();//定义指向初始元素的迭代器
cout << "\nThe first element is:" << *pos;//输出第一个元素
vector<int>::reverse_iterator rpos = col1.rbegin();//应用逆向迭代器指向最后一个元素
cout << "\nThe last element is:" << *rpos << endl;//输出最后一个元素
back_insert_iterator<vector<int>>iter(col1);//声明后插迭代器
*iter = 66;//应用后插迭代器插入元素66
back_inserter(col1) = 88;//应用函数后插入元素88
copy(col1.begin(), col1.end(), output);//输出后插操作后的向量容器col1中的元素
getchar();
getchar();
cout << endl;
}
2、迭代器相关的辅助函数
advance()、distance()两个提供了所有迭代器一些原本只有随机访问迭代器才有的访问能力:前进或后退多少个元素,以及处理迭代器之间的距离。iter_swap()函数允许用户交换两个迭代器的值。
void advance(InputIterator& pos,Dist n);
该函数使输入型迭代器前进(或后退)n个元素,对于双向或随机访问迭代器,可以取负值,表示向后访问。
dist distance(InputIterator pos1,InputIterator pos2);
该函数传回两个输入迭代器pos1和pos2之间的距离,两个迭代器必须指向同一个容器。如果不是随机访问迭代器,则从pos1开始往前走必须能够到达pos2,即pos2的位置必须与pos1相同或在后。
iter_swap()可以交换两个迭代器所指向的元素值。
void iter_swap(ForwardIterator1 pos1,ForwardIterator2 pos2);
该函数用于交换迭代器pos1和pos2所指向的元素值,迭代器的类型不必相同,但所只元素之间必须可以相互赋值。
#include<iostream>
#include<iterator>
#include<list>
#include<algorithm>
using namespace std;
int main() {
int A[] = {1,2,3,4,5};
const int N = sizeof(A) / sizeof(int);
list<int> col1(A, A + N);
ostream_iterator<int> output(cout," ");
cout << "List col1 contains:";
copy(col1.begin(),col1.end(),output);//输出初始列表容器col1中的元素
list<int>::iterator pos = col1.begin();//定义指向初始元素的迭代器
cout << "\nThe first element is:" << *pos;//输出第一个元素
advance(pos,3);//前进3个元素,指向第四个元素
cout << "\nThe 4th element is:" << *pos;//输出第4个元素
cout << "\nThe advanced distance is:" << distance(col1.begin(),pos);//输出当前迭代器位置与初始位置的距离
iter_swap(col1.begin(),--col1.end());//交换列表容器中第一个元素和最后一个元素
cout << "\nAfter exchange list col1 constains:"; copy(col1.begin(),col1.end(),output);//输出交换元素后列表容器col1中的元素
getchar(); getchar();
cout << endl;
}