迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为他们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。
迭代器有各种不同的创建方法。程序可能把迭代器作为一个变量创建。一个STL容器类可能为了使用一个特定类型的数据而创建一个迭代器。作为指针,必须能够使用*操作符类获取数据。你还可以使用其他数学操作符如++。典型的,++操作符用来递增迭代器,以访问容器中的下一个对象。如果迭代器到达了容器中的最后一个元素的后面,则迭代器变成past-the-end值。使用一个past-the-end值得指针来访问对象是非法的,就好像使用NULL或为初始化的指针一样。
#include <vector>
#include <iostream>
#include <list>
#include <string>
#include <set>
#include <algorithm>
#include <fstream>
#include <iterator> //copy
using namespace std;
template <class T>
inline void print (const T& coll, const char* optcstr="")
{
typename T::const_iterator pos;
std::cout << optcstr;
for (pos=coll.begin(); pos!=coll.end(); ++pos)
{
std::cout << *pos << ' ';
}
std::cout << std::endl;
}
void test_back_inster()
{
vector<int> coll;
// create back inserter for coll
// - inconvenient way
back_insert_iterator<vector<int> > iter(coll);
// insert elements with the usual iterator interface
*iter = 1991;
iter++;
*iter = 1992;
iter++;
*iter = 1993;
print(coll);
// create back inserter and insert elements
// - convenient way
back_inserter(coll) = 2013;
back_inserter(coll) = 2014;
print(coll,"use back_inserter\n");
// use back inserter to append all elements again
// - reserve enough memory to avoid reallocation
coll.reserve(2*coll.size());
copy (coll.begin(), coll.end(), // source
back_inserter(coll)); // destination
print(coll,"after reserve and copy\n");
}
/********************
运行结果:
1991 1992 1993
use back_inserter
1991 1992 1993 2013 2014
after reserve and copy
1991 1992 1993 2013 2014 1991 1992 1993 2013 2014
Process returned 0 (0x0) execution time : 0.057 s
Press any key to continue.
**********************/
void test_front_insert()
{
list<string> coll;
// create front inserter for coll
// - inconvenient way
front_insert_iterator<list<string> > iter(coll);
// insert elements with the usual iterator interface
*iter ="how";
iter++;
*iter = "are";
iter++;
*iter = "you";
print(coll);
// create front inserter and insert elements
// - convenient way
front_inserter(coll) = "I'm";
front_inserter(coll) = "fine";
print(coll,"use front_inserter(coll)\n");
// use front inserter to insert all elements again
copy (coll.begin(), coll.end(), // source
front_inserter(coll)); // destination
print(coll,"after copy\n");
}
/****************************
运行结果:
you are how
use front_inserter(coll)
fine I'm you are how
after copy
how are you I'm fine fine I'm you are how
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.
*****************************/
void test_insert()
{
set<int> coll;
// create insert iterator for coll
// - inconvenient way
insert_iterator<set<int> > iter(coll,coll.begin());
// insert elements with the usual iterator interface
*iter = 1991;
iter++;
*iter = 1992;
iter++;
*iter = 1993;
print(coll,"set of coll: \n");
// create inserter and insert elements
// - convenient way
inserter(coll,coll.end()) = 2013;
inserter(coll,coll.end()) = 2014;
print(coll,"set of coll after insert:\n");
// use inserter to insert all elements into a list
list<int> coll2;
copy (coll.begin(), coll.end(), // source
inserter(coll2,coll2.begin())); // destination
print(coll2,"list of coll2 after copy...inserter(coll2,coll2.begin()):\n");
// use inserter to reinsert all elements into the list before the second element
copy (coll.begin(), coll.end(), // source
inserter(coll2,++coll2.begin())); // destination
print(coll2,"list of coll2 after copy...inserter(coll2,++coll2.begin()):\n");
}
/********************************
运行结果:
set of coll:
1991 1992 1993
set of coll after insert:
1991 1992 1993 2013 2014
list of coll2 after copy...inserter(coll2,coll2.begin()):
1991 1992 1993 2013 2014
list of coll2 after copy...inserter(coll2,++coll2.begin()):
1991 1991 1992 1993 2013 2014 1992 1993 2013 2014
Process returned 0 (0x0) execution time : 0.025 s
Press any key to continue.
**********************************/
void test_ostream_iterator()
{
// create ostream iterator for stream cout
// - values are separated by a newline character
ostream_iterator<int> intWriter(cout,"\n");
// write elements with the usual iterator interface
*intWriter = -1949;
intWriter++;
*intWriter = -1978;
intWriter++;
*intWriter = -1911;
// create collection with elements from 1 to 9
vector<int> coll;
for (int i=2007; i<=2013; ++i)
{
coll.push_back(i);
}
cout<<"\nwrite all elements without any delimiter\n";
copy (coll.begin(), coll.end(),ostream_iterator<int>(cout));
cout << endl;
cout<<"\nwrite all elements without delimiter space \n";
copy (coll.begin(), coll.end(),ostream_iterator<int>(cout," "));
cout << endl;
cout<<"\nwrite all elements with < as delimiter\n";
copy (coll.begin(), coll.end(),ostream_iterator<int>(cout," < "));
cout << endl;
cout<<"\n now test string :\n";
vector<string> mystring;
back_inserter(mystring)="please ";
back_inserter(mystring)="believe in ";
back_inserter(mystring)="your ";
back_inserter(mystring)="future!";
copy(mystring.begin(),mystring.end(),ostream_iterator<string>(cout));
}
/**********************************
运行结果如下:
-1949
-1978
-1911
write all elements without any delimiter
2007200820092010201120122013
write all elements without delimiter space
2007 2008 2009 2010 2011 2012 2013
write all elements with < as delimiter
2007 < 2008 < 2009 < 2010 < 2011 < 2012 < 2013 <
now test string :
please believe in your future!
Process returned 0 (0x0) execution time : 0.062 s
Press any key to continue.
************************************/
void test_istream_iterator_1()
{
// create istream iterator that reads integers from cin
istream_iterator<int> intReader(cin);
// create end-of-stream iterator
istream_iterator<int> intReaderEOF;
/* while able to read tokens with istream iterator
* - write them twice
*/
while (intReader != intReaderEOF)
{
cout << "you input is (should be int type):" << *intReader << endl;
++intReader;
}
}
/***************************
运行结果:
1991
you input is (should be int type):1991
2003
you input is (should be int type):2003
2013
you input is (should be int type):2013
wangshihui
Process returned 0 (0x0) execution time : 19.581 s
Press any key to continue.
****************************/
void test_istream_iterator_2()
{
ifstream readTheMisteryMethod( "TheMisteryMethod.txt",ios::app );//追加方式
if(readTheMisteryMethod.is_open())
{
string str;
cout<<"\nTheMisteryMethod.txt is as follow:\n";
getline(readTheMisteryMethod ,str,'\0');//输出缓冲区剩余的字符
cout<< str <<"\n";
readTheMisteryMethod.close();
}
else
cout << "Unable to open file\n";
ifstream readTXT ( "TheMisteryMethod.txt",ios::app );//追加方式
// Outputs to TheMisteryMethod.txt through readTXT
if(readTXT.is_open())
{
istream_iterator<string> cinPos(readTXT);
ostream_iterator<string> coutPos(cout," ");
cout<<"\nwhile input is not at the end of the file\n";
cout<<"write every third string\n\n";
while (cinPos != istream_iterator<string>())
{
// ignore the following two strings
advance (cinPos, 2);
// read and write the third string
if (cinPos != istream_iterator<string>())
{
*coutPos++ = *cinPos++;
}
}
cout << endl;
readTXT.close();
}
else
cout << "Unable to open file\n";
}
/*************************************
运行结果:
TheMisteryMethod.txt is as follow:
To recap,the three main objectives in the Mystery Method are:
To attract a woman
To establish comfort, trust, and connection
To structure the opportunity to be seduced
while input is not at the end of the file
write every third string
three in Method attract To trust, To opportunity seduced
Process returned 0 (0x0) execution time : 0.026 s
Press any key to continue.
**************************************/
int main()
{
return 0;
}
迭代器的类型
对于STL数据结构和算法,你可以使用五种迭代器。下面简要说明了这五种类型:
· Input iterators 提供对数据的只读访问。
· Output iterators 提供对数据的只写访问
· Forward iterators 提供读写操作,并能向前推进迭代器。
· Bidirectional iterators提供读写操作,并能向前和向后操作。
· Random access iterators提供读写操作,并能在数据中随机移动。
尽管各种不同的STL实现细节方面有所不同,还是可以将上面的迭代器想象为一种类继承关系。从这个意义上说,下面的迭代器继承自上面的迭代器。由于这种继承关系,你可以将一个Forward迭代器作为一个output或input迭代器使用。同样,如果一个算法要求是一个bidirectional 迭代器,那么只能使用该种类型和随机访问迭代器。
迭代器(iterator)是一种检查容器内元素并遍历元素的数据类型。
(1) 每种容器类型都定义了自己的迭代器类型,如vector:vector<int>::iterator iter;这条语句定义了一个名为iter的变量,它的数据类型是由vector<int>定义的iterator类型。
(2) 使用迭代器读取vector中的每一个元素:
vector<int> ivec(10,1);
for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();++iter)
{
*iter=2; //使用 * 访问迭代器所指向的元素
}
const_iterator:
只能读取容器中的元素,而不能修改。
for(vector<int>::const_iterator citer=ivec.begin();citer!=ivec.end();citer++)
{
cout<<*citer;
//*citer=3; error
}
vector<int>::const_iterator 和 const vector<int>::iterator的区别
const vector<int>::iterator newiter=ivec.begin();
*newiter=11; //可以修改指向容器的元素
//newiter++; //迭代器本身不能被修改
(3) iterator的算术操作:
iterator除了进行++,--操作,可以将iter+n,iter-n赋给一个新的iteraor对象。还可以使用一个iterator减去另外一个iterator.
const vector<int>::iterator newiter=ivec.begin();
vector<int>::iterator newiter2=ivec.end();
cout<<"\n"<<newiter2-newiter;
STL是C++中重要部分之一(面向对象、STL、模板等),其中三个基本的STL组件包括:
1. 迭代器。迭代器之于容器相当于指针之于数组,提供了访问容器对象的方法,事实上C++中的指针也是一种迭代器,但是要注意迭代器不仅仅是指针,不一定具有地址值。
2. 容器。容器是一种模板类,例如list、vector、deques等,一般由迭代器访问容器中的数据。
3. 算法。STL中数据结构和算法是分离的,各种函数在广义容器中(包括链表、数组、string对象、容器)完全通用。
1.头文件:
STL头文件一般不使用.h扩展,其中主要使用的头文件和对应容器类如下:
#include Container Class
<deque> deque
<list> list
<map> map, multimap
<queue> queue, priority_queue
<set> set, multiset
<stack> stack
<vector> vector
<string> string
<iterator>各种 iterator
<algorithm> 各种算法函数
STL均使用标准命名空间using namespace std。
2.迭代器:
迭代器有五种类型,这五种类型是一种继承关系,具体如下:
Input iterators:提供对数据的只读访问,前向推进。输入迭代器可以使用==和!=来测试是否相等;使用*来访问数据;使用++操作符前向推进。例如find函数需要保证输入迭代器。
Output iterators:提供对数据的只写访问,前向推进。输出迭代器缺省只写,由于该迭代器无法读取对象,因此不会在任何搜索和其他算法中使用它。
Forward iterators:提供读写访问,前向推进。例如replace函数需要保证前向迭代器。
Bidirectional iterators:提供读写访问,前向或后向推进。例如reverse函数需要保证双向迭代器。
Random access iterators:提供读写访问,随机移动(非const的指针也是随机迭代器)。STL中的排序和搜索函数使用随机访问迭代器,随机访问迭代器可以使用关系操作符做比较。
除此之外,还包括一些特殊的迭代器:
指针迭代器:一个指针也是一种迭代器。
常量迭代器:对于只读变量,为了防止错误赋值,可以使用常量迭代器const_iterator,该迭代器指向的对象不允许改变。注意:const ***<***>::iterator的含义是该迭代器成为常量,不可指向其他数据,与常量迭代器的含义是不一样的。
3.流迭代器
将输入输出(例如标准输入输出流cin/cout或者文件流等)作为容器看待,因此接受迭代器参数的算法都可以和流一起工作。
STL定义模板类ostream_iterator作为输出流迭代器,其构造函数有两个参数,包括一个ostream对象和一个string值(作为间隔符),因此可以象下面一样创建一个迭代器:
ostream_iterator<int>(cout, “\t”) //定义cout迭代器
ofstream out(“text.txt”);
ostream_iterator<string> obegin(out, “\n”); //定义文件流输出迭代器
STL定义模板类istream_iterator作为输入流迭代器,可指定读取的来源,并应该和结束迭代器比较。具体如下:
istream_iterator<int> intreader(cin); //定义cin流迭代器
isteam_iterator<int> eof;
copy(istream_iterator<string>(cin), istream_iterator<string>(), 输出迭代器); //定义无变量名的cin流迭代器
ifstream in(“text.txt”);
istream_iterator<string> ibegin(in);
istream_iterator<string> iend; //定义文件流输入迭代器
还有一些具体应用如下:
//利用流迭代器填充vector
{
ifstream in("test.txt");
istream_iterator<string> ibegin(in);
istream_iterator<string> iend;
vector<string> vec(ibegin, iend);
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, "\n"));
}
//利用输入流填充vector
{
vector<string> vec;
copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vec));
sort(vec.begin(), vec.end());
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout,"\n"));
}
//利用流迭代器保存vector内容到文件
{
ifstream in("test.txt");
istream_iterator<string> ibegin(in);
istream_iterator<string> iend;
vector<string> vec(ibegin, iend);
ofstream out("testcopy.txt");
copy(vec.begin(), vec.end(), ostream_iterator<string>(out, "\n"));
}
注意:上面用输入流迭代器来初始化vector后,不可再用这个输入流迭代器,因为随着数据的读取,迭代器已经到达输入流或者文件流的末尾了。
4.插入迭代器:
int arr[] = {1, 2, 3, 4, 5};
vector<int> vi;
copy(arr, arr + 5; vi.begin());
该语句不会执行,因为没有为vi分配存储空间。此时使用插入迭代器可以将值插入到容器中,自动为vi扩展存储空间,主要包括三种插入迭代器。
普通插入器:将对象插入到容器任何对象的前面。该迭代器使用容器的insert操作符替代赋值运算符,第一个参数是容器本身,第二个参数是容器迭代器指定插入位置。
Front inserters:将对象插入到数据集的前面,例如链表表头。该迭代器使用push_front操作替代赋值运算符,参数是容器本身。
Back inserters:将对象插入到数据集的尾部,例如vector的尾部,导致vector容器扩展。该迭代器调用容器的push_back操作替代赋值运算符,参数是容器本身。
注意:使用插入迭代器可能导致容器中的其他对象移动位置,因此现有的迭代器变成非法,需要重新赋值(list除外,不受影响)。
int arr[] = {1, 2, 3, 4, 5};
vector<int> vi;
copy(arr, arr + 5; front_iterator(vi));
//最终结果按序是5 4 3 2 1,因为每次调用push_front将一个数据插入到vi的前面。
Vector<int>::iterator p = find(vi.begin(), vi.end(), 2);
copy(arr, arr + 2, inserter(vi, p));
//最终结果是5 4 3 1 2 2 1,因为调用insert一次性将所有数据插入到p前。
5.混合迭代器函数:
下面两个迭代器函数非常有用:
advance(iterator, int):按照指定的数目增减迭代器。第一个参数是迭代器,第二个参数是增减的数目(前向迭代器该数必须为正,双向或者随机迭代器该数可以为负)。
distance(iterator, iterator, int&):返回到达一个迭代器所需递增操作的数目。该函数是递归的,每次递归第三个参数,因此必须初始化该参数为0然后使用该函数。
五种迭代器之间的关系
vector 和deque提供的是RandomAccessIterator,list提供的是BidirectionalIterator,set和map提供的 iterators是 ForwardIterator,关于STL中iterator迭代器的操作如下:
说明:每种迭代器均可进行包括表中前一种迭代器可进行的操作。
迭代器操作 说明
(1)所有迭代器
p++ 后置自增迭代器
++p 前置自增迭代器
(2)输入迭代器
*p 复引用迭代器,作为右值
p=p1 将一个迭代器赋给另一个迭代器
p==p1 比较迭代器的相等性
p!=p1 比较迭代器的不等性
(3)输出迭代器
*p 复引用迭代器,作为左值
p=p1 将一个迭代器赋给另一个迭代器
(4)正向迭代器
提供输入输出迭代器的所有功能
(5)双向迭代器
--p 前置自减迭代器
p-- 后置自减迭代器
(6)随机迭代器
p+=i 将迭代器递增i位
p-=i 将迭代器递减i位
p+i 在p位加i位后的迭代器
p-i 在p位减i位后的迭代器
p[i] 返回p位元素偏离i位的元素引用
p<p1 如果迭代器p的位置在p1前,返回true,否则返回false
p<=p1 p的位置在p1的前面或同一位置时返回true,否则返回false
p>p1 如果迭代器p的位置在p1后,返回true,否则返回false
p>=p1 p的位置在p1的后面或同一位置时返回true,否则返回false
插入迭代器
插入迭代器(inserter iterator)是一个可以访问序列容器vector<T>、deque<T>和list<T>添加新元素的迭代器。有3个创建插入迭代器的模板:
Back_insert_iterator<T>在类型T的容器末尾插入元素。容器必须提供push_back()函数。
Front_insert_iterator<T>在类型T的容器开头插入元素。同样push_front()对要求可用。
Insert_iterator<T>在类型T的容器内从指定位置开始插入元素。这要求容器有一个insert()函数,此函数接受两个参数,迭代器作为第一个实参,要插入的项作为第二个实参。
前两个插入迭代器类型的构造函数接受一个指定要在其中插入元素的容器的实参。如:
list<int> numbers;
front_insert_iterator<list<int> > iter(numbers);
向容器中插入值:
*iter = 99;
也可以将front_inserter()函数用于numbers容器:
front_inserter(numbers) = 99;
这几行代码为numbers列表创建了一个前段插入器,并用它在开头插入99。front_inserter()函数的实参是运用迭代器的容器。
insert_iterator<T>迭代器的构造函数需要两个实参:
insert_iterator<vector<int> > iter(numbers,numbers.begin());
该构造函数的第二个实参是一个指定在何处插入数据的迭代器。向此容器赋值:
for (int i = 0; i < 100; i++)
*iter = i + 1;
代码执行后,前100个元素的值依次为100,99,…,1。
输出流迭代器
为了补充输入流迭代器模板,ostream_iterator<T>模板提供了向输出流写类型T的对象的输出流迭代器。
ostream_iterator<int> out(cout);
该模板的实参int指定要处理的数据类型,构造函数实参cout指定将作为数据的目的地的流,以便cout迭代器能将int类型的值写到标准输出流中。如:
int data [] = {1,2,3,4,5,6,7,8,9};
vector<int> numbers(data,data+9);
copy(numbers.begin(),numbers.end(),out);
在algorithm头文件中定义的copy()算法将由前两个迭代器实参指定的对象序列复制到第三个实参指定的输出迭代器。此代码执行结果为:123456789.
但现在写到标准输出流中的值没有空格。第二个输出流迭代器构造函数能解决这一点:
ostream_iterator<int> out(cout,” ”);
现在将输出1 2 3 4 5 6 7 8 9