迭代器
迭代器用于存取一个序列中的元素,其用法类似于指针(实际上迭代器就可以看做是泛化的指针)。不同的是,迭代器比指针更为抽象,它可以指向容器中的一个位置,而我们不必关心这个位置对应的真正物理地址。
iterator从操作方法分可分为如下5类:
Input iterator | Read, but not write; increment only |
Output iterator | Write, but not read; increment only |
Forward iterator | Read and write; increment only |
Bidirectional iterator | Read and write; increment and decrement |
Random access iterator | Read and write; full iterator arithmetic |
下面的concept继承关系图可以更清楚的说明这5类之间的关系,越下面的迭代器,使用越灵活,操作方法也越多。
对于Input Iterator和output iterator,常用的实例就是流迭代器,分别是:istream_iterator和ostream_iterator。它们的构造函数如下:
istream_iterator<T> in(strm); | Create istream_iterator that reads objects of type T from input stream strm . |
istream_iterator<T> in; | Off-the-end iterator for istream_iterator . |
ostream_iterator<T> in(strm); | Create ostream_iterator that writes objects of type T to the output stream strm . |
ostream_iterator<T> in(strm, delim); | Create ostream_iterator that writes objects of type T to the output stream strm using delim as a separator between elements. delim is a null-terminated character array. |
对于两个输入流迭代器,要进行相等性比较必须具有同样的类型。如果它们都指向流结束,那么它们相等,如果它们都没有指向流结束,那么它们只要指向同一个流就相等,这个特性常常被用在判断流结束上。下面是使用输入流迭代器的一个例子:
istream_iterator<int> in_iter(cin); // read ints from cin
istream_iterator<int> eof; // istream "end" iterator
vector<int> vec(in_iter, eof); // construct vec from an iterator range
向前迭代器没有输入输出迭代器的对于自增操作和写入(或读取)操作必须交替进行的限制,它可以自由的进行读写操作,不过只能自增,不能自减。
双向迭代器比向前迭代器更灵活,它在其基础上加入了自减的操作。向map,set,list这些容器提供的都是双向迭代器。
随机访问迭代器从名字上就可以看出来,它支持对容器的随机访问等操作,在双向迭代器的基础上,它还支持p+=n,p-=n,p+n,p-n,p1-p2,p1 or p2等操作。对于像string,vector,deque这些容器提供的都是随机访问迭代器,可以看出这些容器都是采用连续数据结构的。
以上对迭代器的划分是按照他们的concept分类进行划分的,实际上,我们还可以从它的使用角度上进行划分,例如逆向迭代器。通过container.rbegin(),container.rend()这两个容器函数可以得到容器的逆向迭代器,它的操作方式和普通的迭代器正好相反,我们可以用它们对容器进行逆向迭代。
使用逆向迭代器一定要注意它真正指向的元素,请看下面这个例子,假设有这么一个字符串line:
FIRST,MIDDLE,LAST
现在要打印出最后一个单词,我们可能会如此写C++代码:
string::reverse_iterator rcomma = find(line.rbegin(), line.rend(), ',');
cout << string(line.rbegin(), rcomma) << endl;
但打印结果却是:TSAL
仔细想想,这个结果是很正常的,因为逆向迭代器本来就是对容器的逆向迭代。但这可不是我们想要的,要想得到last这个打印结果,我们需要如下修改代码:
cout << string(rcomma.base(), line.end()) << endl;
调用逆向迭代器的base函数可以得到对应的正向迭代器,有些童鞋看到这里可能会对其迭代范围感到疑惑,因为它并没有打印出逗号,实际上rcoma和rcoma.base()并没有指向同一个元素,STL这样设计的原因主要是考虑到序列的左闭右开原则,使得rcomma.base(), line.end()和line.rbegin(), rcomma指向的是同一段序列,或许下面的图说明的更加清晰一些:
额,这图怎么老消失?