STL迭代器学习总结
1.迭代器的本质与简介
1.1迭代器的本质
迭代器从C++11开始正式写入STL。迭代器是一个可遍历STL容器全部或部分元素的对象。迭代器用来表现容器中的某一个位置。
从上面这个描述中我们可以看出,迭代器实际上是存放地址,因此迭代器的本质是一个指针。那么我们之前对指针进行的一些操作在迭代器中同样适用,比如使用“*”来取出迭代器所指代的内容等常规操作。
1.2迭代器的简介
迭代器是所谓的smart pointer,具有遍历复杂数据结构的能力。其内部运作机制取决于其所遍历的数据结构。因此,每一种容器都必须提供自己的迭代器。事实上,每一种的确都将迭代器以嵌套的方式定义与class内部。因此,虽然各种迭代器的接口相同,类型却各不相同。
2.迭代器的使用
2.1 迭代器头文件
所有容器都定义有各自的iterator类型,所以使用某种容器的iterator时,不必包含什么特殊的头文件。不过有些特别的iterator,例如reverse iterator以及若干辅助的iterator函数,被定为在头文件中。因此,当你需要比寻常的container iterator和其类型更多的东西时,应该包含这个头文件。
2.2 迭代器的种类及使用
2.2.1 Output Iterator
Output迭代器是向前写入,其提供者是Ostream和inserter。
Output迭代器允许一步一步前行并搭配write动作。因此你可以一个一个元素地赋值,不能使用output地带器对同一区间迭代两次。事实上,甚至不能保证将一个value赋值两次而其迭代器却不累进。
output迭代器无须比较操作。你无法检验output迭代器是否有效,或写入是否成功。唯一可做的就是写入、写入、再写入。
表达式 | 效果 |
---|---|
*iter = val | 将val写入迭代器所指位置 |
++iter | 向前步进,返回新位置 |
iter++ | 向前步进,返回旧位置 |
TYPE(iter) | 复制迭代器(copy构造函数) |
2.2.2 Input Iterator
input迭代器只能一次一个以前行方向读取元素,按此顺序一个个返回元素值。
表达式 | 功能 |
---|---|
*iter | 读取实际元素 |
iter->member | 读取实际元素的成员(如果有的话) |
++iter | 向前步进。返回新位置 |
iter++ | 向前步进,返回旧位置 |
iter1 == iter2 | 判断两个迭代器是否相等 |
iter1 != iter2 | 判断两个迭代器是否不等 |
TYPE(iter) | 复制迭代器(copy构造函数) |
input迭代器只能读取元素一次。如果复制input迭代器,并令原input迭代器和新产生的拷贝都向前读取,可能会遍历到不同的值。
2.2.3 Forward Iterator
使用forward迭代器时需要引入头文件<forwarf_list>,即**#include<foward_list>**
forward迭代器是一种input迭代器,且在前进读取时提供额外保证。
表达式 | 功能 |
---|---|
*iter | 访问实际元素 |
iter->member | 访问实际元素的成员 |
++iter | 向前步进,返回新位置 |
iter++ | 向前步进,返回旧位置 |
iter1==iter2 | 判断两个迭代器是否相等 |
iter1 != iter2 | 判断两个迭代器是否不等 |
TYPE() | 创建迭代器 |
TYPE(iter) | 复制迭代器 |
iter1 = iter2 | 对迭代器赋值 |
和input迭代器不同的是,两个forward迭代器如果指向同一个元素,operator==会获得true,如果两者都增加,会再次指向同一个元素。
2.2.4 Bidrectional Iterator(双向迭代器)
Bidirectional迭代器在forward迭代器的基础上增加了回头迭代能力,因此它支持递减操作符,可一步一步后退。
表达式 | 功能 |
---|---|
–iter | 步退,返回新位置\ |
iter– | 步退,返回旧位置 |
2.2.5 Random-Access Iterator(随机访问迭代器)
Random-access迭代器在bidirectional迭代器的基础上增加了随机访问能力。因此它必须提供iterator算术运算。也就是说它能增减某个偏移量,计算距离,并运用>和<等关系运算符进行比较。
新增操作:
表达式 | 功能 |
---|---|
iter[n] | 访问索引位置为n的元素\ |
iter += n | 前进n个元素(如果n为负数,则改为后退) |
iter -= n | 后退n个元素(如果n为负数,则改为前进) |
iter+n | 返回iter之后的第n个元素 |
n+iter | 返回iter之后的第n个元素 |
iter-n | 返回iter之前的第n个元素 |
n - iter | 返回iter之前的第n个元素 |
iter1 - iter2 | 返回iter1和iter2之间的距离 |
iter1<iter2 | 判断iter1是否在iter2之前 |
iter1>iter2 | 判断iter1是否在iter2之后 |
iter1 <=iter2 | 判断iter1是否不在iter2之后 |
iter1>=iter2 | 判断iter1是否不在iter2之前 |
可以提供随机访问迭代器的容器有:
array,vector,deque,string,wstring,pointer
2.3 迭代器的相关辅助函数
2.3.1 advance()
advance()可将迭代器的位置增加,增加的幅度由实参决定,也就是说它令迭代器一次前进(或后退)多个元素。
#include<iterator>
/*
*将迭代器pos的位置增加n
*@param pos:迭代器
*@param n:增加的幅度,n可为负数,表示向后退
*/
void advance(InputIterator& pos,Dist n);
advance函数并不检查地带器是否超过序列的end(),因为迭代器通常不知道其所操作的容器。所以调用advance有可能导致不明确行为,因为对序列尾端调用operator++是一种未被定义的行为。
示例:
#include <iterator>
#include <list>
using namespace std;
int main(){
list<int> coll;
//初始化列表
for (int i = 1; i <= 9; i++) {
coll.push_back(i);
}
//获取列表头部迭代器
list<int>::iterator pos = coll.begin();
cout << *pos << endl;
//迭代器向后移动3个位置
advance(pos, 3);
cout << *pos << endl;
//迭代器向前移动一个位置
advance(pos, -1);
cout << *pos << endl;
return 0;
}
2.3.2 next()和prev()
1. next()函数定义
C++11提供了两个新增的雇主函数,允许你前进/后退移动迭代器的位置。
#include<iterator>
ForwardIterator next(ForwardIterator pos);//前进一个位置
ForwardIterator next(ForwardIterator pos,Dist n);//前进n个位置
如果处理的是bidirectional和random-access迭代器,n可为负值,导致后退移动。next并不检查是否会跨越序列的end(),因此调用者必须自行保证其结果有效。
2.prev()函数定义
#include<iterator>
BidirectionalIterator prev(BidirectionalIterator pos);//后退一个位置
BidirectionalIterator prev(BidirectoinalIterator pos,Dist n);//后退n个位置
prev函数不会检查是否会跨越序列的begin(),因此调用者必须自行保证其结果有效。
2.3.3 distance() 函数
distance函数用来处理两个迭代器之间的距离。其函数定义为:
#include <iterator>
//返回两个input迭代器的距离,两个迭代器必须指向同一个容器
//如果不是random-access迭代器,则pos2的位置必须不能在pos1之前
Dist distance (InputIterator pos1,InputIterator pos2);
这个函数能够根据迭代器种类采取最佳实现手法,这必须利用迭代器标志才能达成。对于random-access迭代器,此函数仅仅是返回pos2-pos1,因此具备常量复杂度,而对于其他迭代器,distance函数会不断递增pos1,直到抵达pos2为止,然后返回递增的次数。也就是说对于其他迭代器种类,distance具备线性复杂度。因此对于non-random-access迭代器而言,distance的效能并不好,应该避免使用。‘
示例:
#include<iterator>
#include <list>
using namespace std;
int main(){
list<int> coll;
//初始化列表,从-3到9插入列表
for (int i = -3; i <= 9; i++) {
coll.push_back(i);
}
list<int>::iterator pos;
//查找值为5的元素的位置
pos = find(coll.begin(),coll.end(), 5);
//判断是否找到了
if (pos != coll.end()) {
//如果找到了就计算距离开始位置的距离
cout << "difference between beginning and 5:" << distance(coll.begin(), pos) << endl;
}
else {
cout << "value not found" << endl;
}
system("pause");
return 0;
}
2.3.4 iter_swap()
这个函数是用来交换连个迭代器所指的元素值。
#include<iterator>
//交换迭代器pos1和pos2所指的值
//迭代器的类型不必相同,但其所指的两个值必选可以相互赋值
void iter_swap(ForwardIterator1 pos1,ForwardIterator2 pos2);
示例:
#include <iterator>
#include <list>
using namespace std;
int main(int argc,char *argv[]){
list<int> coll;
//初始化列表,插入1-9
for (int i = 1; i <= 9; i++) {
coll.push_back(i);
}
//输出列表中所有元素
for (auto e : coll) {
cout << e << " ";
}
cout << endl;
//交换第一个和第二个元素的值
iter_swap(coll.begin(), next(coll.begin()));
//输出交换后列表中所有的元素
for (auto e : coll) {
cout << e << " ";
}
cout << endl;
//交换第一个和最后一个元素的值
iter_swap(coll.begin(), prev(coll.end()));
for (auto e : coll) {
cout << e << " ";
}
cout << endl;
return 0;
}
结果为:
1 2 3 4 5 6 7 8 9
2 1 3 4 5 6 7 8 9
9 1 3 4 5 6 7 8 2
2.4 迭代器适配器
此类特殊适配器使得算法能够以反向或安插模式进行工作,也可以和stream搭配工作。
2.4.1 Reverse迭代器
Reverse迭代器重新定义了递增运算和递减运算,使其行为正好颠倒。因此如果你使用这类迭代器,算法将以反向次序处理元素。所以标准容器都允许使用reverse迭代器来遍历元素。
示例:
#include <iterator>
#include <list>
#include <algorithm>
/*
*用来作为for_each函数的回调函数
*@param elem:需要输出的内容
*@return 没有返回值
*/
void print(int &elem) {
cout << elem << " ";
}
/*
*用来测试Reverse迭代器
*@param 没有参数
*@return 没有返回值
*/
void testReverseiterator() {
//创建列表并初始化
list<int> coll = { 1,2,3,4,5,6,7,8,9 };
//输出列表中所有元素
for_each(coll.begin(), coll.end(), print);
cout << endl;
//逆序输出所有列表元素
for_each(coll.rbegin(), coll.rend(), print);
cout << endl;
}
int main(int argc,char *argv[]){
//测试Reverse Iterator
testReverseiterator();
return 0;
}
结果为:
1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1
2.4.2 Insert迭代器
Insert迭代器,用来将赋予新值动作转换为安插新值动作。借由这种迭代器,算法将执行安插行为而非覆写行为。所有insert迭代器都属于output迭代器种类,所以它只提供赋值能力。
1. Insert迭代器的种类
C++标准库提供了三种insert迭代器:back inserter、front inserter和general inserter。它们之间的区别在于安插的位置。事实上,它们各自调用所属容器中不同的成员函数,所以insert迭代器初始化时一定要清楚指明所属的容器是哪一种。
不同insert迭代器及其能力:
名称 | class名称 | 其所调用的函数 | 生成函数 |
---|---|---|---|
Back inserter | back_insert_iterator | push_back(value) | back_inserter(cont) |
Front inserter | front_insert_iterator | push_front(value) | front_inserter(cont) |
General inserter | insert_iterator | insert(pos,value) | inserter(cont,pos) |
容器本身必须支持insert迭代器所调用的函数,否则这种insert迭代器就不可以用。
2. 示例
Back Inserter
#include <iterator>
#include <vector>
#include <algorithm>
using namespace std;
/*
*用来作为for_each函数的回调函数
*@param elem:需要输出的内容
*@return 没有返回值
*/
void print(int &elem) {
cout << elem << " ";
}
/*
*用来测试Back Inserter
*@param 没有参数
*@return 没有返回值
*/
void testBackInserter() {
vector<int> coll;
//创建back inserter
back_insert_iterator<vector<int>> iter(coll);
//使用back inserter向数组中插入元素
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
//输出数组中所有的元素
for_each(coll.begin(), coll.end(), print);
cout << endl;
//更方便的方法
back_inserter(coll) = 44;
back_inserter(coll) = 55;
for_each(coll.begin(), coll.end(), print);
cout << endl;
//使用backinserter向后面再次添加coll中所有元素
//reserve是预定的意思,这里相当于预定两倍coll数组的大小
coll.reserve(2 * coll.size());
copy(coll.begin(), coll.end(), back_inserter(coll));
//再次输出coll数组中所有的元素
for_each(coll.begin(), coll.end(), print);
cout << endl;
}
int main(int argc,char *argv[]){
//测试Back Inserter
testBackInserter();
return 0;
}
结果为:
1 2 3
1 2 3 44 55
1 2 3 44 55 1 2 3 44 55
Front Inserter
#include <list>
#include <iterator>
#include <algorithm>
using namespace std;
/*
*用来作为for_each的回调函数,功能是输出元素
*@param 需要输出的元素
*@return 没有返回值
*/
void print(int elem){
cout << elem << " ";
}
/*
*用来测试Front Inserter
*@param elem:没有参数
*@return 没有返回值
*/
void testFrontInserter() {
list<int> coll;
//不方便的方法
front_insert_iterator<list<int>> iter(coll);
//使用front inserter迭代器插入元素
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
//输出coll链表中所有元素
for_each(coll.begin(), coll.end(), print);
cout << endl;
//方便的方法
front_inserter(coll) = 44;
front_inserter(coll) = 55;
for_each(coll.begin(), coll.end(), print);
cout << endl;
//使用front insert迭代器将coll所有元素再插入一次
copy(coll.begin(), coll.end(), front_inserter(coll));
for_each(coll.begin(), coll.end(), print);
cout << endl;
}
int main(int argc,char *argv[]){
//测试front inserter
testFrontInserter();
return 0;
}
结果为:
3 2 1
55 44 3 2 1
1 2 3 44 55 55 44 3 2 1
General Inserter
General Inserter根据 两个实参完成初始化:(1)容器,(2)带插入的位置。迭代器内部以“带安插位置”为实参调用成员函数insert()。便捷函数inserter()则提供了更方便的手段来产生general inserter并加以初始化。
General Inserter对所有标准容器均使用(除array和forward外),因为这些容器都提供有insert()成员函数。general inserter安插动作完成后,获得刚安插的那个元素的位置。
示例:
#include <set>
#include <list>
#include <iterator>
using namespace std;
/*
*用来作为for_each的回调函数,功能是输出元素
*@param 需要输出的元素
*@return 没有返回值
*/
void print(int elem){
cout << elem << " ";
}
/*
*用来测试general inserter
*@param 没有参数
@return 没有返回值
*/
void testGeneralInserter() {
set<int> coll;
//为coll创建insert iterator
insert_iterator<set<int>> iter(coll,coll.begin());
//使用迭代器插入元素,这种不是便捷的方法
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
//输出所有元素
for_each(coll.begin(), coll.end(), print);
cout << endl;
//使用便捷方法
inserter(coll, coll.end()) = 44;
inserter(coll, coll.end()) = 55;
for_each(coll.begin(), coll.end(), print);
cout << endl;
//使用inserter向一个list中插入所有元素
list<int> coll2;
copy(coll.begin(), coll.end(), inserter(coll2, coll2.begin()));
for_each(coll2.begin(), coll2.end(), print);
cout << endl;
}
int main(int argc,char *argv[]){
//测试General Inserter
testGeneralInserter();
return 0;
}
结果为:
1 2 3 1 2 3 44 55 1 2 3 44 55
2.4.3 Stream(串流)迭代器
Stream迭代器是一种迭代器适配器。借由它,可以把stream当成算法的来源端和目的端。更明确的说,一个istream迭代器可用来从input stream读取元素,而一个osream迭代器可以用来对output stream写入元素。
Stream迭代器的一种特殊形式,是所谓stream buffer迭代器,用来对stream buffer进行直接读取和涂写操作。
1. Ostream
Ostream迭代器可以将被赋值写入output stream。用了它,算法就可以直接写入stream。建立Ostream迭代器是,必须提供一个output stream对象作为实参,迭代器会把value写至该output stream。
表达式 | 功能\ |
---|---|
ostream_iterator(ostream) | 为Ostream建立一个ostream迭代器 |
ostream_iterator(ostream,delim) | 为ostream建立一个ostream迭代器,各元素之间以delim为分隔符,delim的类型是const char* |
*iter | 返回iter |
iter = value | 将value写到ostream |
++iter/iter++ | 返回iter |
示例:
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
/*
*用来测试ostream迭代器
*@param 没有参数
*@return 没有返回值
*/
void testOstream() {
//创建ostream迭代器
ostream_iterator<int> intWriter(cout, "\n");
*intWriter = 42;
intWriter++;
*intWriter = 77;
intWriter++;
*intWriter = -5;
//创建数组并初始化数组
vector<int> coll = { 1,2,3,4,5,6,7,8,9 };
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout));
cout << endl;
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout, "<"));
cout << endl;
}
int main(int argc,char *argv[]){
//测试Ostream迭代器
testOstream();
return 0;
}
结果为:
42
77
-5
123456789
1<2<3<4<5<6<7<8<9<
2. Istream迭代器
Istream迭代器是ostream迭代器的搭档,用来从input stream读取元素。和ostream一样,在建立Istream迭代器的时候,必须提供一个input stream作为实参,迭代器将从其中读取数据。然而,读取动作可能失败,可以使用一个所谓的end-of-stream迭代器。只要有任何一次读取失败,所有Istream迭代器都会变成end-of-stream迭代器。所以进行一次读取后,就应该和end-of-stream迭代器比较一番,看看这个迭代器是否依然有效。
示例:
/*
*用来测试Istream迭代器
*@param 没有参数
*@return 没有返回值
*/
void testIstream() {
//创建Istream迭代器
istream_iterator<int> intReader(cin);
//创建end-of-stream迭代器
istream_iterator<int> intReaderEOF;
//当Istream可以读时
//读两次
while (intReader != intReaderEOF) {
cout << "once: " << *intReader << endl;
cout << "once again: " << *intReader << endl;
++intReader;
}
}
2.4.4 Move(搬移)迭代器
Move迭代器用来将任何对地岑元素的处理转换为一个move操作。
例如:
std::list<std::string> s;//字符串链表
std::vector<string> v1(s.begin(),s.end());//将字符串链表复制到v1
std::vector<string> v2(make_move_iterator(s.begin()),make_move_iterator(s.end()));//将字符串移到v2中