迭代器(iterator )
迭代器是对对象的间接访问。并不是所有类型都可以使用迭代器,只有容器可以。
因此迭代器的对象为:容器中的元素 or string对象中的字符。
有效的迭代器是指 迭代器 指向某个元素 or 指向尾元素的下一位置,其他情况都是无效的。
一、迭代器的使用
有迭代器的类型 就有 可以返回迭代器的成员,最常见的如begin( )和end( ),begin( )用于返回指向第一个元素的迭代器,end( )用于返回指向尾元素的下一位置的迭代器。
当容器( string对象 )为空时,begin( ) 和 end( ) 都返回尾元素的下一位置的迭代器。
一个迭代器的范围是由一对迭代器表示的,即begin和end,两个迭代器指向同一个容器,分别指向首元素和尾元素的下一位置,这种范围是一个左闭合区间:[begin,end)。
1. 迭代器运算符
运算符 | 操作 |
---|---|
*it | 得到 it 所指向对象(字符)的引用 |
it->num | 得到it所指向对象的num成员 |
++it | 使it指向下一个元素(字符)) |
it1 == it2 | 判断 两个迭代器所指元素是否相同 或 都指向尾元素的下一位置 |
vector<int> num = { 2,4,6,8 };
for (auto b = num.begin(), e = num.end(); b != e; ++b)
cout << *b << endl;
其中,forward_list迭代器不支持“- -”运算符。
2. 迭代器类型
你肯定会有疑问,迭代器是什么类型的呢?事实是,我们只需要知道它是支持我们访问元素的工具就可以,不需要在意它是什么类型。
在拥有迭代器的类型中,使用iterator和const_iterator来表示迭代器的类型。其中,const_iterator类似于指向常量的指针,对迭代器指向的元素只能读。
vector<int>::iterator it; //读写元素
string::iterator it2; //读写字符
vector<string>::const_iterator it3; //读元素,不可通过迭代器写元素
在默认条件下,当迭代器所指对象是常量时,begin和end的返回类型是const_iterator,否则是iterator。
而有些时候,比如只需要读数据的时候,最好使用常量类型,C++11提供给了两个获得常量类型迭代器的函数:cbegin( ) 和 cend( ) 。
3. 任一改变vector对象容量的操作,都会使迭代器失效
至于为什么,以后会说到。
二、迭代器运算
string、vector、deque、array类型的迭代器,在上述运算符的基础之上提供了更多的运算符。
运算符 | 操作 |
---|---|
it + n | 将it向前移动n个元素 |
it1 - it2 | 两个迭代器必须为同一容器(string对象)中的,表示距离的差值 |
< > >= <= | 判断两个迭代器所指元素(字符)位置的先后 |
使用迭代器实现二分搜索
vector<int> num = { 2,4,6,8,10,11 };
vector<int>::const_iterator b = num.cbegin() , e = num.cend();
vector<int>::const_iterator mid = b + (e - b) / 2;
while (*mid != 4 && mid != e)
{
if (*mid < 4)
b = mid + 1;
else e = mid - 1;
mid = b + (e - b) / 2;
}
三、迭代器失效
向容器中添加或删除元素、改变容器大小、赋值操作等都有可能会使容器的迭代器失效。
向容器添加元素:
- 容器为vector、string。若添加元素之后,重新分配内存,则指向容器的迭代器、引用、指针全部失效。若添加元素之后,不重新分配内存,则添加位置之前的迭代器、引用、指针有效,之后的无效。
- 容器为deque。若在首尾添加元素,迭代器失效,指针、引用不失效。若不在首尾添加元素,迭代器、指针、引用均失效。
- 容器为list、forward_list。则迭代器、指针、引用都不会失效。
从容器删除元素:
- 容器为vector、string。删除元素位置之前的迭代器、引用、指针有效,之后的无效。尾后迭代器总是失效。
- 容器为deque。若在首尾删除元素,迭代器失效,指针、引用不失效。若不在首尾删除元素,迭代器、指针、引用均失效。
- 容器为list、forward_list。则迭代器、指针、引用都不会失效。
我们在程序实现中,一定要考虑到迭代器失效的问题。