1.迭代器
容器迭代器的作用类似于数据库中的游标(cursor),它屏蔽了底层存储空间的不连续性,在上层使容器元素维持一种“逻辑连续”的假象。不可把迭代器与void*和“基类指针”这样的通用指针混淆。
指针代表真正的内存地址,即对象在内存中的存储位置;而迭代器则代表元素在容器中的相对位置。
STL把迭代器划分为5个类别(Category),这5类迭代器分别具有不同的能力,表现为支持不同的运算符,它们都是类模版,因此具有通用性。
标准迭代器
迭代器种类 | 提供的操作 | 特征说明 |
trivial迭代器 | X x; X(); *x; *x=t; X->m
|
只是一个概念,用以区分所有迭代器的定义。 |
输入迭代器 Input Iterator | *i; (void)i++; ++i; *i++; 还包含trivial 迭代器的所有操作
|
提供只读操作,即可读取它所指向的元素的值,但不可改变元素的值; 如果i==j,并不意味++i==++j; |
输出迭代器 Output Iterator |
X x; X(); X(x); X y(x); X y=x; *x=t; ++x; (void)x++; *x++=t;
|
提供只写操作,即可改变它所指向的元素的值;但不可读取该元素的值
|
前进迭代器 Forward Iterator |
++i; i++;
|
只能向前访问下一个元素,不能反向访问前一个元素 典型:slist |
双向迭代器 (Bidirectional Iterator) |
++i; i++; i--; --i; 还包含前进迭代器中所有操作
|
它是对前进迭代器的扩充,提供双向访问 典型:list(双向链表),set/map |
随机访问迭代器 (Random Access Iterator) |
i+=n; i+n或n+i; i-=n; i-n; i-j; i[n]; i[n]=t; 还包含双向迭代器中的所有操作
|
能访问前面或后面第n个元素,即可以随机访问任何一个元素 典型:vector的迭代器(它就是原始指针),deque |
(2)迭代器失效及其危险性
迭代器失效是指当前容器底层存储发生变动时,原来指向容器中某个或某些元素的迭代器由于元素的存储位置发生了改变而不再指向它们,从而成为无效的迭代器。使用无效的迭代器就像使用无效的野指针一样危险。
可能引起容器存储变动的操作:reserve()、resize()、push_back()、pop_back()、insert()、erase()、clear()等容器方法和一些泛型算法如sort()、copy()、replace()、remove()、unique(),以及集合操作(并、交、差)算法等。如下例:
/***************************************************************/
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ages; // 未预留空间
ages.push_back(2); //引起内存重分配
vector<int>::const_iterator p = ages.begin();
for (int i = 0; i<10; i++)
{
ages.push_back(5); //会引起若干次内存重分配操作
}
cout << "The first age:" << *p << endl; //p已经失效,危险!
return 0;
}
/***************************************************************/
解决迭代器失效问题:(1)在调用上述操作后重新获取迭代器;(2)在修改容器钱为其预留足够的空闲空间可以避免存储空间重分配。
上例可改为:
/***************************************************************/
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ages; // 未预留空间
ages.push_back(2); //引起内存重分配
vector<int>::const_iterator p = ages.begin();
for (int i = 0; i<10; i++)
{
ages.push_back(5); //会引起若干次内存重分配操作
}
p = ages.begin();//重新获取
cout << "The first age:" << *p << endl;
return 0;
}
迭代器是广义指针,而指针满足所有的迭代器要求。迭代器是STL算法的接口,而指针是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。例如,可将STL算法用于数组。假设要对一个double数组进行排序
double a[100];
STL sort()函数接受指向容器第一个元素的迭代器和指向超尾的迭代器作为参数。&a[0]或a是第一个元素的地址,&a[10]或a+100是数组最后一个元素后面元素的地址,因此可以sort(a,a+100);
C++确保了表达式a+100是被定义的,只要表达式的结果位于数组中,因此C++支持将超尾概念用于数组。由于指针是迭代器,而算法是基于迭代器的,所以使得STL算法可以用于常规数组。
指针和迭代器就像是苹果和水果的区别,苹果属于水果(指针属于迭代器),但是水果并不都是苹果。