16.3 STL库迭代器
STL标准库中迭代器(Iterator)与指针很类似。当作用于相应的容器,可以实现遍历、修改容器元素等操作。STL中迭代器是单独实现的组件,用户可以根据需要定义作用于不同容器的迭代器。下面将会分小节讲述STL标准库中提供的各种迭代器。
16.3.1 STL迭代器简介
STL标准库中总共提供了五类不同迭代器类型,每个迭代器类型都针对不同的处理需求提供了相应的功能供开发者根据不同的情况使用。下面分别简单介绍下五类迭代器基本情况。
q 第一类迭代器称为输入迭代器(Input Iterator)。该类迭代器主要用于从容器当中取数据,支持单向从容器首部位置向后递增遍历。该类迭代器相对较为灵活,可以通过变量递增方式实现指向的跨越访问,并且该类迭代器也支持相应的比较运算操作。
q 第二类迭代器称为输出迭代器(Output Iterator)。该类迭代器主要用于向容器中写入数据,同样也只能单次递增的从容器的首部向后遍历元素。
q 第三类迭代器称为正向迭代器(Forward Iterator)。该类迭代器结合了输入/输出两种迭代器功能,同时还可以记录该迭代器在容器中的位置信息,以供后续处理。
q 第四类迭代器称为双向迭代器(Bidirectional Iterator)。该类迭代器不仅仅拥有了输入输出迭代器的功能,同时还支持从容器尾部向首部反向遍历的操作,并且支持可以多次遍历容器的功能。
q 第五类称为随机访问迭代器(Random Access Iterator)。它可以根据需要,随机地访问容器中的元素,并且可以正向或者反向的任意递增间隔的跳转。
从上述介绍看来,五类迭代器从基本的输入输出开始,一直到随机访问迭代器,其提供的功能越来越强大。基本上所有的STL容器都支持上述五种不同类别的迭代器操作。根据实际应用的需要,用户来选择不同类别的迭代器。初学者可以将迭代器看作指针来访问具体的容器内容。实际上,STL迭代器就是针对访问容器的指针的封装。
16.3.2 输入/输出迭代器
输入/输出迭代器用于处理容器的数据的读取与写入操作。
q 输入迭代器为应用程序处理容器中的数据提供了操作接口。使用该迭代器,可以从容器序列中读取元素值。该迭代器本身可以通过++运算符改变指向(前置自增、后置自增方式都支持),并且可以使用比较运算符进行比较运算。
q 输出迭代器提供了接收外部程序处理的数据,往容器序列中写入这些数据。另外该迭代器也可以通过递增运算来改变其位置指向(前置自增、后置自增方式),并且也可以被引用输出或者参与程序其它部分操作。
输入、输出迭代器划分主要从迭代器支持的功能上进行区别,输入迭代器支持后置自增操作(point++)、前置自增(++point)、迭代器间接运算符(*point,只作为表达式右值使用)、迭代器之间赋值(point1=point2)以及迭代器比较操作(point1==point2,point1!=point2);输出迭代器支持后置自增和前置自增操作以外,还支持迭代器间接运算符(*point,作为表达式左值,用于存放数据)以及迭代器赋值操作(point1=point2)。
了解了输入输出迭代器基本概念之后,通过一个完整实例来演示两种迭代器具体操作以及之间的使用区别。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1609.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1609
* 源文件chapter1609.cpp
* list容器输入输出迭代器基本操作实例
*/
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
//第一部分代码
stringdata,data1; //定义字符串两个对象,分别为data和data1
list<string> stringList; //定义字符串类型的List队列对象
cout<<"Please input some stringvalues into StringList('#' is end):"<<endl; //提示输入字符串类型的List元素
cin>>data; //从键盘输入字符串变量
//第二部分代码
while(data!= "#") //判断输入以‘#’为结束符,如果输入字符串不为#,就会不断加入List
{
stringList.push_front(data); //通过List容器push_front方法,将输入字符串变量加入List头部
cout<<"Please input some stringvalues into StringList('#' is end):"<<endl; //提示继续输入字符串变量值
cin>>data; //从键盘输入字符串变量
}
cout << "StringList value's: "; //提示打印输出字符串类型的List里面元素值
list<string>::iterator iter,iter1; //通过iterator定义两个字符串类型List迭代器
iter1 = stringList.end(); //迭代器iter1通过赋值的方式指向List最后一个元素
//第三部分代码
for (iter = stringList.begin(); iter != iter1;iter++) //通过迭代器iter循环遍历List各个元素
{
cout << *iter; //通过间接运算符输出遍历到List的迭代器指向的值
*iter = "Hello"; //将迭代器*iter作为左值在表达式中出现,将常量字符串Hello作为右值
data1 = *iter; //将赋值后的迭代器iter通过间接运算符将其作为右值赋给字符串对象data1
}
cout << endl; //循环遍历结束后,添加输出结束换行符
cout<<"data1:"<<data1<<endl; //打印输出字符串对象data1内容
return0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1609.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1609.o
CC=g++
chapter1609: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1609
clean:
rm-f chapter1609 core $(OBJECTS)
submit:
cp-f -r chapter1609 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1609.o chapter1609.cpp
g++ chapter1609.o -g -o chapter1609
[developer @localhost src]$ make submit
cp -f -r chapter1609 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1609
Please input some string values into StringList('#'is end):
jack
Please input some string values intoStringList('#' is end):
lily
Please input some string values intoStringList('#' is end):
lihua
Please input some string values intoStringList('#' is end):
hanmm
Please input some string values into StringList('#'is end):
#
StringList value's: hanmmlihualilyjack
data1:Hello
4.剖析程序
本实例主要通过STL中提供的List容器来演示输入/输出迭代器实现的基本功能。STL库中的迭代器的不同类型都是通过iterator来统一定义的。不同种类的迭代器主要体现在不同的容器上面。容器List是支持双向迭代器功能的,因为双向迭代器包含了输入/输出迭代器的功能,因此此实例中主要通过List来演示输入输出的迭代器功能使用。下面将会通过
第一部分代码实例程序主要在主函数内部实现,具体代码实现首先通过标准的字符串类string定义两个字符串对象data和data1。通过STL中list定义字符串类型的容器对象stringList,随后通过输出流对象cout提示用户输入字符串变量值。然后通过输入流对象cin将键盘输入的字符串变量值放入字符串对象data中。
第二部分代码通过while循环结构,在其中条件判断中判断字符串对象data内容是否为结束符“#”。如果不为“#”,则在循环体的内部通过容器list的push_front方法将字符串对象data内容放入到容器的头部。随后再通过cout和cin对象来提示和输入数据。
代码中通过cout对象提示程序接下来会将容器list中的元素数据循环打印出来。随后,通过iterator关联容器list定义出字符串类型的迭代器对象iter和iter1。STL的list字符串类型容器对象stringList通过end()方法将指向该容器的最后一个元素的迭代器返回,通过赋值运算将该迭代器赋值给迭代器iter1,即此时迭代器iter1指向了容器对象stringList最后一个元素。
第三部分代码通过for循环结构,循环中首先将list容器对象stringList的第一个元素通过begin()方法,将指向容器对象stringList首元素的迭代器赋值给迭代器对象iter,判断当前的iter指向元素是否与迭代器对象iter1中的尾部元素相同。如果不同,则执行for循环结构体中的代码片段,之后通过iter++运算遍历下一个元素。
程序for循环体的内部,首先通过cout输出流对象输出迭代器中指向的数据元素值,在输出流中通过间接运算符来取得迭代器iter中的数据元素值并打印出来。之后的代码通过将*iter作为表达式的左值来存放常量字符串“Hello”,并且将重新赋值后的*iter作为表达式右值赋值给字符串对象data1。
循环体处理结束后,通过endl来实现打印数据换行,同时打印输出字符串对象data1的内容。上述代码程序通过make执行之后编译无误会产生相应的可执行程序,在程序的bin目录中执行可执行程序,根据屏幕输出提示,敲入字符串变量将会按照迭代器获取到的数据打印情况来验证程序处理逻辑。
总结上述实例,迭代器提供的输入、输出、正向、双向和随机类型都是通过iterator关联相应的容器定义对象时就决定了哪类迭代器,不同的STL容器具备的迭代器类型不同,如上述容器list支持双向类型迭代器。需要注意的是,输入/输出迭代器除了上述统一对外定义方式以外,还提供了特殊的符合输入、输出迭代器功能的迭代器istream_iterator(输入类型迭代器)和(输出类型迭代器)。该部分迭代器涉及文件流操作,此处暂时不过多讲述,初学者只需要理解对外统一的迭代器操作使用方式即可。
16.3.4 双向迭代器
STL库中双向迭代器除了同时具备输入/输出迭代器功能以外,还支持从容器尾部向首部反向遍历的操作以及通过“--”运算符支持前置递减和后置递减的操作。支持双向迭代器功能的容器除了前面实例介绍的list以外,还有set、map容器都是支持双向迭代器操作。
下面将会通过STL中map容器的上双向迭代器操作实例,演示双向迭代器在具体容器中使用情况。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1610.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1610
* 源文件chapter1610.cpp
* map容器双向迭代器基本操作实例
*/
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
//第一部分代码
map<int,string> stringMap,stringMap1; //定义两个map对象,其中键为int整型数据,键值为字符串数据
string data; //定义字符串对象data
int i= 1; //定义整型变量i,同时初始化为数值1
cout<<"Please input some stringvalues into stringMap('#' is end):"<<endl; //提示输入map值,以#为结束符
cin>>data; //从键盘输入字符串变量data
while(data != "#") //通过while循环判断输入字符串是否为“#”
{
stringMap[i]= data; //不为结束符,通过数组方式来将输入的字符串变量值放入map中
cout<<"Please input some stringvalues into stringMap('#' is end):"<<endl; //提示输入map值
cin>>data; //从键盘输入字符串变量data
i++; //将map中的键i通过后置自增方式增加1
}
cout << "stringMap value's:"<<endl; //提示打印输出map内容值
map<int, string>::iterator iter; //定义同类型的map迭代器为iter
for (iter = stringMap.begin();iter != stringMap.end();iter++) //for循环中,通过iter迭代器遍历map容器
{
cout << (*iter).first <<" -> "; //输出流打印输出map容器中的键
cout << (*iter).second ; //输出流打印输出map容器中的键对应的值
}
cout<<endl; //循环遍历结束后,添加输出结束换行符
//第二部分代码
i = 1; //整型变量i,重新初始化为数值1
cout<<"Please input some stringvalues into stringMap1('#' is end):"<<endl; //提示输入map值
cin>>data; //从键盘输入字符串变量data
while(data != "#") //通过while循环判断输入字符串是否为“#”
{
stringMap1.insert(map<int,string>::value_type (i, data)); //通过容器map的insert方法插入元素数据
cout<<"Please input some stringvalues into stringMap1('#' is end):"<<endl;// 提示输入map值
cin>>data; //从键盘输入字符串变量data
i++; //将map中的键i通过后置自增方式增加1
}
cout << "stringMap1 value's:"<<endl; //提示打印输出map内容值
map<int, string>::reverse_iterator iter1; //定义同类型的map反向迭代器为iter
for (iter1 = stringMap1.rbegin();iter1 !=stringMap1.rend(); iter1++) // for循环中,通过iter1迭代器反向
//遍历map容器
{
cout << (*iter1).first <<" -> "; //输出流打印输出map容器中的键
cout << (*iter1).second ; //输出流打印输出map容器中的键对应的值
}
cout<<endl; //循环遍历结束后,添加输出结束换行符
return 0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1610.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1610.o
CC=g++
chapter1610: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1610
clean:
rm-f chapter1610 core $(OBJECTS)
submit:
cp-f -r chapter1610 ../bin
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1610.o chapter1610.cpp
g++ chapter1610.o -g -o chapter1610
[developer @localhost src]$ make submit
cp -f -r chapter1610 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1610
Please input some string values into stringMap('#'is end):
jack
Please input some string values into stringMap('#'is end):
john
Please input some string values into stringMap('#'is end):
lily
Please input some string values into stringMap('#'is end):
poli
Please input some string values into stringMap('#'is end):
#
stringMap value's:
1 -> jack
2 -> john
3 -> lily
4 -> poli
Please input some string values intostringMap1('#' is end):
lilei
Please input some string values intostringMap1('#' is end):
lijie
Please input some string values intostringMap1('#' is end):
lijia
Please input some string values intostringMap1('#' is end):
lily
Please input some string values intostringMap1('#' is end):
#
stringMap1 value's:
4 -> lily
3 -> lijia
2 -> lijie
1 -> lilei
4.剖析程序
上述实例程序主要通过演示双向迭代器具体操作,来提供双向迭代器一般应用方法。实例程序主要分为两个部分剖析讲解。
第一部分代码实例程序主要在主函数内部实现,具体代码实现首先定义两个map容器对象stringMap和stringMap1。该map容器键值分别为整型值和对应的字符串变量。通过string定义一个字符串对象data,另外通过int定义整型变量i,同时初始化值为1。
随后通过输出流对象cout提示用户输入字符串变量值。然后通过输入流对象cin将键盘输入的字符串变量值放入字符串对象data中。接下来通过while循环结构,在其中条件判断中判断字符串对象data内容是否为结束符“#”。如果不为“#”,则在循环体的内部通过类似数组方式下标将数据放入map容器中。由于map是键值成对的存储方式,因此左值中以数组方式组织,i变量为键即key。表达式右值为字符串对象data,即键对应的值value。通过这种数组式的组织数据方式,将字符串类型的数据放入map容器。随后再通过cout和cin对象来提示和输入数据,同时将键(key)自增1。
代码中通过cout对象提示程序接下来会将容器map中的元素数据循环打印出来,随后通过iterator关联容器map定义出相同类型的迭代器对象iter。通过for循环结构,循环条件中首先将map容器对象stringMap的第一个元素通过begin()方法,将指向容器对象stringMap首元素的迭代器赋值给迭代器对象iter,判断当前的iter指向元素是否与stringMap尾部元素相同。如果不同,则执行for循环结构体中的代码片段,之后通过iter++运算遍历下一个元素。
程序for循环体的内部,首先通过cout输出流对象输出迭代器指向的数据元素键(key)、值(value),在输出流中通过间接运算符“*”来取得迭代器iter中first和second数据值打印出来。循环体处理结束后,通过endl来实现打印数据换行。
第二部分代码在通过迭代器iter遍历完map容器stringMap之后,将整型变量i重新初始化为整型值1。接着通过输出流对象cout提示用户输入字符串变量值。然后通过输入流对象cin将键盘输入的字符串变量值放入字符串对象data中。接下来通过while循环结构,在其中条件判断中判断字符串对象data内容是否为结束符“#”,如果不为“#”在循环体的内部通过map容器的insert方式入数据到map容器中。随后再通过cout和cin对象来提示和输入数据,同时将键(key)自增1。
代码中通过cout对象提示程序接下来会将容器map中的元素数据循环打印出来,随后通过反向迭代器reverse_iterator关联容器map定义出相同类型的迭代器对象iter1。通过for循环结构,循环条件中首先将map容器对象stringMap的最后一个元素通过rbegin()方法,将指向容器对象stringMap尾部元素的迭代器赋值给迭代器对象iter1,判断当前的iter1指向元素是否与stringMap首元素相同,如果不同则执行for循环结构体中的代码片段,之后通过iter1++运算遍历下一个元素。
上述代码程序通过make执行之后编译无误会产生相应的可执行程序,在程序的bin目录中执行可执行程序,根据屏幕输出提示,敲入字符串变量将会按照迭代器获取到的数据打印情况来验证程序处理逻辑。
16.3.5 随机访问迭代器
随机访问迭代器(RandomAccess Iterator),可以根据需要随机的访问容器中的元素,并且可以正向或者反向的任意递增间隔的跳转。
STL库中随机迭代器除了同时具备输入、输出迭代器、双向迭代器功能以外,还支持随机访问,特别是这种迭代器拥有iterator运算,即你可以对一个随机类型的迭代器进行加、减、比较等操作。容器vector、deque和string容器的迭代器属于随机迭代器。
随机迭代器除了支持前面输入、输出、双向迭代器功能以外,还会根据随机访问的特性支持迭代器偏移递增i位(point+=i),迭代器偏移递减i位(point-=i),迭代器直接返回指向元素的i位(point[i]),迭代器比较运算操作(point1<point2,point1<=point2,point1>point2,point1>=point2)。
下面将会通过STL中vector容器的上随机迭代器操作实例,演示随机迭代器在具体容器中使用情况。
1.准备实例
打开UE工具,创建新的空文件并且另存为chapter1611.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。
/**
* 实例chapter1611
* 源文件chapter1611.cpp
* vector容器随机迭代器基本操作实例
*/
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//第一部分代码
vector<int>intvec; //定义整型vector对象intvec
intdata; //定义整型变量data
cout<<"Pleaseinput some int values into intvec('0' is end):"<<endl; //提示输入map值,以0为结束符
cin>>data; //从键盘输入整型变量data
while(data != 0) //通过while循环判断输入整型值是否为0
{
intvec.push_back(data); //不为结束符,通过vector的push_back方法将data放入容器尾部
cout<<"Please input some intvalues into intvec('0' is end):"<<endl; //提示输入map值,以0为结束符
cin>>data; //从键盘输入整型变量data
}
cout << "intvec value's:"<<endl; //提示打印输出vector内容值
vector<int>::iterator iter; //定义整型vector随机迭代器iter
for (iter = intvec.begin();iter !=intvec.end(); iter++) // for循环中,通过iter迭代器遍历vector容器
{
cout << *iter<<endl; //通过间接运算符输出遍历到vector的迭代器指向的值
}
//第二部分代码
vector<int>intvec1; //定义整型vector对象intvec1
vector<int>::iterator iter1 =intvec1.begin(); //定义整型vector随机迭代器iter,同时指向intvec1首元素
intvec1.insert(iter1, intvec.begin(),intvec.end());//通过vector的insert方法将容器intvec内容插入容器intvec1
iter1 = intvec1.begin() + 3; //将随机迭代器iter1指向容器intvec1的第三个位置
intvec1.insert(iter1, 1, 100); //容器intvec1通过insert方法将其中第三个元素替换为100
iter1 = intvec1.begin() + 1; //重新将迭代器iter1指向容器intvec1[1]的位置
intvec1.erase(iter1, iter1 + 2); //容器intvec1通过erase方法将intvec1[1]~intvec1[2]元素删除
cout << "intvec value's:"<<endl; //提示打印输出vector内容值
for (iter1 = intvec1.begin();iter1 <intvec1.end(); iter1++) // for循环中,通过iter1迭代器遍历vector容器
{
cout << *iter1<<endl; //通过间接运算符输出遍历到vector的迭代器指向的值
}
return 0;
}
2.编辑makefile
Linux平台下需要编译源文件为chapter1611.cpp,相关makefile工程文件编译命令编辑如下所示。
OBJECTS=chapter1611.o
CC=g++
chapter1611: $(OBJECTS)
$(CC)$(OBJECTS) -g -o chapter1611
clean:
rm-f chapter1611 core $(OBJECTS)
submit:
cp-f -r chapter1611 ../bin
上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。
3.编译运行程序
当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。
[developer@localhost src]$ make
g++ -c -ochapter1611.o chapter1611.cpp
g++ chapter1611.o -g -o chapter1611
[developer @localhost src]$ make submit
cp -f -r chapter1611 ../bin
[developer @localhost src]$ cd ../bin
[developer @localhost bin]$ ./chapter1611
Please input some int values into intvec('0' isend):
123
Please input some int values into intvec('0' isend):
234
Please input some int values into intvec('0' isend):
345
Please input some int values into intvec('0' isend):
456
Please input some int values into intvec('0' isend):
0
intvec value's:
123
234
345
456
intvec value's:
123
100
456
4.剖析程序
随机迭代器基本支持的功能在STL迭代器简介中已经简单介绍过,本实例主要演示了这些迭代器基本功能。实例程序主要在主函数内部实现,下面将会通过将程序分为两个部分来进行剖析讲解。
第一部分代码实现首先定义整型vector容器对象intvec。通过int定义一个整型变量data。随后通过输出流对象cout提示用户输入整型变量值。然后通过输入流对象cin将键盘输入的整型变量值放入整型变量data中。接下来通过while循环结构,在其中条件判断中判断整型变量data内容是否为结束符整型值0,如果不为整型值0则在循环体的内部通过vector中的push_back方法将data加入到容器intvec的尾部。
代码中通过cout对象提示程序接下来会将容器vector中的元素数据循环打印出来,随后通过iterator关联容器vector定义出相同类型的随机迭代器对象iter。通过for循环结构,循环条件中首先将vector容器对象intvec的第一个元素通过begin()方法,将指向容器对象intvec首元素的迭代器赋值给迭代器对象iter。判断当前的iter指向元素是否与intvec尾部元素相同,如果不同则执行for循环结构体中的代码片段,之后通过iter++运算遍历下一个元素。
第二部分代码片段中再次定义整型vector向量对象intvec1,同时定义同类型的随机迭代器iter1,并且将该迭代器指向容器intvec1的首元素。通过vector容器的insert方法,将之前定义的容器intvec从首元素到尾部元素都插入到新容器intvec1中去。
从将vector容器intvec所有元素都插入到新容器intvec1中后面的代码片段将会主要演示随机迭代器的特有功能。首先通过容器的begin方法返回指向容器intvec1的首元素迭代器加上3后,将迭代器iter1定位到容器intvec1的第三个位置上。
随后通过容器的insert方法将容器intvec1的第三个元素之后插入值100,之后通过intvec1的begin方法返回的首位元素的迭代器加上1,重新将迭代器iter1定位到容器intvec1的第一个位置上。随后通过erase方法,从容器intvec1第一个位置到第二个位置之间的元素删除。
最后通过cout对象提示将会遍历输出新容器所有元素,并且打印输出出来。照旧使用的for循环控制结构,内部循环条件不同的是迭代器判断是通过小于号来进行遍历范围限定的,通过迭代器之间的比较运算,将新容器intvec1内部的所有元素都遍历输出。
上述代码程序通过make执行之后编译无误会产生相应的可执行程序,在程序的bin目录中执行可执行程序,根据屏幕输出提示,敲入整型变量将会按照迭代器获取到的数据打印情况来验证程序处理逻辑。