迭代器
所有标准库容器都可以使用迭代器,string也支持迭代器。类似于指针类型,迭代器也提供了对对象的间接访问,其对象是容器中的元素和string中的字符。使用迭代器可以访问某个元素,也能从一个元素移动到另一个元素。
迭代器有有效和无效之分,有效的迭代器指向某个元素或者指向容器末尾元素的下一位置。其他情况都属于无效。
- 使用迭代器
有迭代器的类型同时拥有返回迭代器的成员。这些类型都拥有名为begin和end的成员,其中begin负责返回指向第一个元素的迭代器。end则指向尾元素的下一个位置的迭代器,即指向的是一个本不存在的尾后元素,故end成员返回的迭代器被称作尾后迭代器。
特殊情况下,若容器为空,则begin和end返回的是同一个迭代器。
auto b = v.begin(), e = v.end(); //b表示v的第一个元素,e表示v尾元素的下一位置
标准容器迭代器的运算符:
*iter 返回迭代器iter所指元素的引用
iter->mem 解引用iter并获取该元素的名为mem的成员,等价(*iter).mem
++iter 令iter指示容器中的下一个元素
–iter 令iter指示容器中的上一个元素
iter_1==iter_2 判断两个迭代器是否相等,如果指示同一个元素或者是同一个容器的尾后迭代器,则True 反之,不相等
iter_1!=iter_2
迭代器可以解引用获取所指示的元素。也可以对其递增递减。但是end并不实际指向某个元素,所以不能进行解引用和递增。
下面的例子使用迭代器将string的小写字符更改为大写:
string s("some string");
if(s.begin() != s.end()) //防止string或容器为空
auto it = s.begin();
for(;it != s.end() && !isspace(*it);it++)
*it = toupper(*it);
关键概念:泛型编程
学习C语言后学习c++,会对for循环中使用!=感到奇怪,其原因和c++程序员更喜欢使用迭代器而非下标的原因一样:这种编程风格在标准库提供的所有容器上都有效。
- 迭代器类型
拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型(我们无需知道迭代器的精确类型):
vector<int>::iterator it; //it能读写vector<int>的元素;
string::iterator it2; //it2能读写string对象中的元素;
vector<int>::const_iterator it3; //it3只能读元素
string::const_iterator it4; //it4只能读元素
-
begin和end运算符
begin和end的返回类型对应指向的对象是否是常量。C++11新标准引入了cbegin和cend函数,其指向对象和begin,end相同,但返回值都是const_iterator。 -
结合解引用和成员访问操作
如果迭代器所指的对象类型是类,就有可能希望进一步访问它的成员。例如,对于一个由字符串组成的vector对象来说,想要检查其元素是否为空,令it为该vector的迭代器,只需检查it所值字符串是否为空就可以了:
(*it).empty() //解引用it,然后调用结果对象的empty成员
*it.empty() //错误:试图访问it的名为empty的成员,但it是一个迭代器,没有empty成员
补充:成员函数是定义为类的一部分函数,有时也被称为方法*。我们通常用一个类对象的名义来调用成员函数。使用点运算符(.)来表达我们需要“名为(it)的对象的empty成员”。点运算符只能用于类类型的对象。其左为一个类类型的对象,其右为该类型的成员名,运算结果为右侧运算对象指定的成员。当用点运算符访问一个成员函数时,同时我们是想调用该函数。我们使用调用运算符(())来调用一个函数。调用运算符是一对圆括号,里面放置实参列表(可能为空)。
为了简化上述表达式,C++语言定义了箭头运算符(->)。箭头运算符把解引用和成员访问两个操作结合在了一起,也就是说it->mem和(*it).mem表达的意思相同。
- 某些对vector对象的操作会使迭代器失效
我们知道,vector对象可以动态地增长(push_back等方法)。但是会有一些副作用,比如范围for循环中不能向vector对象添加元素。另一个限制是任何一种可能改变vector对象容量的操作,比如push_back,都会使vector对象的迭代器失效。 - 迭代器运算
所有迭代器都支持递增运算,也能用==和!=对任意标准库类型的两个有效迭代器进行比较。
string和vector的迭代器提供了更多的额外运算符:
iter + n
iter - n
迭代器加上或减去一个整数值仍得到一个迭代器,迭代器指示的新位置与原来相比向前/向后移动了若干个元素。
iter1 += n
迭代器加法的复合赋值语句,将 iter1 加上 n 的结果赋给 iter1
iter1 -= n
同加法相同理解
iter1 - iter2
两个迭代器相减的结果是他们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后将得到左侧的迭代器。
参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置
>, >=, <, <=
迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说明前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。 - 使用迭代器运算
下面例子使用迭代器完成二分搜索:
/*
text必须是有序的
beg和end表示我们搜索的范围
*/
vector<int> text{ 2,25,26,69,87,102,233,346 };
int sought;
cin >> sought;
auto beg = text.begin();
auto end = text.end();
auto mid = text.begin() + (end - beg) / 2;
while (mid != end && *mid != sought) {
if (*mid > sought) //我们要搜索的元素在前半部分吗
end = mid; //如果是,则舍去后半部分
else
beg = mid+1;
mid = beg + (end - beg) / 2;
}
if (*mid == sought)
cout << "found out!!" << endl;
if (mid == end)
cout << "Not found" << endl;