C++菜鸟学习笔记系列(9)
本期主题:迭代器介绍
我们在C++菜鸟学习笔记系列(7)、C++菜鸟学习笔记系列(8)中分别介绍了C++语言标准库类型string,vector 的定义及使用。
对于string类型的对象我们可以通过范围for语句和索引的方式访问其中的元素;对于vector类型的对象我们也可以通过下标运算符的方式访问其中的元素。这些方式都可以很好的帮助我们实现对象中的元素,但是对于标准库中的其他几种容器来说,上述的方式却不支持。
其实除了上述的方式外C++语言还为我们提供了一种更加通用的机制迭代器为我们实现同样的目的。这种方式对于所有的标准库容器都可以使用。(虽然string类型并不是容器,但是string类型支持很多与容器类型相似的操作,vector类型支持下标运算符,这一点和string类型相似,string类型支持迭代器这一点和vector类型相似)。
对于迭代器而言,其对象是容器中的元素或者string对象中的某个字符。使用迭代器可以访问某一个元素,也可以从一个元素移动到另外一个元素。
与指针类似的,迭代器也有有效和无效之分,有效的迭代器指向某个元素或者指向容器中尾元素的下一个位置,其他的所有情况都是属于无效。
1.迭代器中的begin & end
与指针不同的是,获取迭代器不是使用取地址符&,有迭代器的类型同时拥有返回迭代器的成员。例如,这些类型都同时拥有begin 和 end的成员。其中begin成员负责返回指向第一个元素的迭代器,end成员则负责返回指向容器尾元素下一位置的迭代器,即该迭代器指示的是容器一个本不存在的“尾后”元素。这样的迭代器没有什么实际含义,只是一个标识而已,表示我们已经处理完了容器中的所有元素。end成员返回的迭代器通常被称为尾后迭代器或者检查为尾迭代器。
注意:特殊情况下,如果容器为空,则begin和end返回的是同一个迭代器,都为尾迭代器。
下面我们看一下迭代器的begin和 end成员具体是如何声明的:
vector <int> v; auto b = v.begin(); auto e = v.end();
一般来说,我们并不清楚(不在意)迭代器的准确类型是什么。在上面的例子中使用auto关键字定义变量b和e,则这两个变量的类型就是begin和 end成员的返回值类型。
2.迭代器运算符
C++语言也为我们提供了许多迭代器支持的运算符,帮助我们在实际应用中更好的使用。
下面我们就介绍一些其中比较常用的运算符。
*iter; // 返回迭代器iter所指元素的引用 iter -> mem; // 解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem ++iter; // 令iter指示容器中的下一个元素 --iter; // 令iter指示容器中的上一个元素 iter1 == iter2; // 判断两个迭代器是否相等 iter1 != iter2; // 判断两个迭代器是否不相等
这些运算符都是非常常见的,而且在其他类型的对象中也大都支持这些运算符,下面我们来看一个实际应用中的小例子:
/* Author: wxc_1998 Date: 2018/10/2 */ #include <iostream> #include <vector> #include <string> using namespace std; void main() { string s = "hello WORLD!"; for(auto iter = s.begin(); iter != s.end() && !isspace(*iter); ++iter ) *iter = toupper (*iter); cout << "the result is: "<< s << endl; cout << "press any key to continue!"; cin.clear(); cin.sync(); cin.get(); }
从上述代码中我们可以看出我们使用了一个for循环用于遍历 s 中的字符,直到我们遇到空格或者到达终点时则跳出循环。在这个循环中我们使用了迭代器每次移动到下一个元素,当然我们使用下标运算符也是可以实现相应的功能的。
小知识点:我们在C语言编程时常常使用 < 运算符来判断是否到达了终点,但是在这个循环中我们仔细观察一下不难发现我们使用了 != 运算符进行判断,这是因为在C++语言中所有的容器都支持 != 运算符而只有一小部分容器支持< 运算符。同时我们后续常常使用迭代器而不使用下标运算符也与之类似,所有的容器都支持迭代器操作,但是只有一小部分容器支持下标运算符的使用。
3.迭代器的两种类型
一般来说我们不知道(也并不需要知道)迭代器的精确类型。实际上。那些拥有迭代器的标准库类型使用 iterator 和 const_iterator 来表示迭代器的类型。如下所示:
vector <int>::iterator it1; // it1 可以读写vector <int>的元素 string ::iterator it2; // it2 可以读写string中的字符 vector <int>::const_iterator it3; // it3 只能读vector <int>的元素,不能写 string ::const_iterator it4; // it4只能读string中的字符,不能写
const_iterator 类似于一个常量指针,能读取但不能修改它所指的元素值,相反iterator却可读可写。
4.迭代器运算
我们在上一节中介绍了迭代器中的++和- -运算符可以帮助我们每次移动一个元素。所有的标准库容器都支持递增运算的迭代器,也能用==或!=进行比较等操作。而string和vector的迭代器为我们提供了更多额外的运算符,一方面可以使得迭代器的每次移动跨过多个元素,另外也支持迭代器进行关系运算。
下面我们看一下string和vector迭代器所支持的运算。
图片来源:C++ primer 表3.7
下面我们通过一个小例子来看一下迭代器运算在二分法搜索中的应用。
/* Author: wxc_1998 Date: 2018/10/2 */ #include <iostream> #include <vector> using namespace std; void main() { vector <int> t; int i; cout << "Please enter the integer number in order!" << endl; while (cin >> i) //enter ctrl + z to end { t.push_back(i);//Add an element to 't' } cin.clear();// must clear the buffer first cin.sync(); int sought = 0; cout << "The element you want to search for :" << endl; cin >> sought; auto be = t.begin(), en = t.end(); auto mid = t.begin() + (en-be)/2; while (be != en && *mid != sought) { if (sought < *mid) en = mid; else be = mid; mid = be + (en-be)/2; } if (sought == *mid) cout << "we find the number : " << sought; else cout << "we not find the number : " << sought; cout << endl << "press any key to continue!" << endl; cin.clear(); cin.sync(); cin.get(); }
假如我们输入数据:
1 23 45 51 55 67 89 92 ctrl+z 回车
23 回车
则输出
我们在这里主要关注的是迭代器的运算和移动操作,关于二分法算法的原理比较简单我这里就不过过叙述了,大家若对二分法搜索有什么不明白可以自行百度了解。
好了,这次就写到这里了,我们下次再见。
注:虽然这篇博客的内容十分简单,但是大家若有转载还请标明出处!
还有大家若对博客中的内容有任何问题可以随时联系我提问。