STL实践与分析
--再谈迭代器【中】
二、iostream迭代【续】
3、ostream_iterator对象和ostream_iterator对象的使用
能够使用ostream_iterator对象将一个值序列写入流中,其操作过程与使用迭代器将一组值逐个赋值给容器中的元素同样:
ostream_iterator<string> out_iter(cout,"\n");
istream_iterator<string> in_iter(cin),eof; while (in_iter != eof)
{
*out_iter++ = *in_iter++;
}
读取cin,并将每一个读入的值依次写入到cout中不同的行中。
4、在类类型上使用istream_iterator
提供了输入操作符>>不论什么类型都能够创建istream_iterator对象。
istream_iterator<Sales_item> item_iter(cin),eof;
Sales_item sum;
sum = *item_iter ++; while (item_iter != eof)
{
if (item_iter -> same_isbn(sum))
{
sum += *item_iter;
}
else
{
cout << sum << endl;
sum = *item_iter;
}
++ item_iter;
}
cout << sum << endl;
5、流迭代器的限制
1)不可能从ostream_iterator对象读入,也不可能写到istream_iterator对象中。
2)一旦给ostream_iterator对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator对象中每一个不同的值都仅仅能正好输出一次。
3)ostream_iterator没有 ->操作符。
6、与算法一起使用流迭代器
因为算法是基于迭代器操作实现的,而流迭代器定义了一些迭代器操作。因为流迭代器操作,因此,至少可在一些泛型算法上使用这类迭代器。
//从文件里读取一些数,再将读取的不反复的数写到标准输出
ifstream inFile("input");
istream_iterator<int> in_iter(inFile),eof; vector<int> iVec(in_iter,eof);
sort(iVec.begin(),iVec.end()); ostream_iterator<int> out_iter(cout," ");
unique_copy(iVec.begin(),iVec.end(),out_iter);
//另外一种方式输出
vector<int>::iterator end_unique = unique(iVec.begin(),iVec.end());
cout << endl;
for (vector<int>::iterator iter = iVec.begin(); iter != end_unique; ++iter)
{
*out_iter ++ = *iter;
}
//P353 习题11.16
int main()
{
ifstream inFile("input"); istream_iterator<string> in_iter(inFile),eof;
ostream_iterator<string> out_iter(cout," "); while (in_iter != eof)
{
*out_iter ++ = *in_iter ++;
}
}
//习题11.17
int main()
{
ifstream inFile("input"); istream_iterator<int> in_iter(inFile),eof;
vector<int> iVec(in_iter,eof); for (vector<int>::iterator iter = iVec.begin(); iter != iVec.end(); ++iter)
{
cout << *iter << endl;
}
}
//习题11.18 本题集合了iostream迭代器,文件流
int main()
{
istream_iterator<int> in_iter(cin),eof;
ofstream outFile1("output1"),outFile2("output2"); ostream_iterator<int> out_iter1(outFile1," "),out_iter2(outFile2,"\n"); while (in_iter != eof)
{
if (*in_iter % 2)
{
*out_iter1 ++ = *in_iter ++;
}
else
{
*out_iter2 ++ = *in_iter ++;
}
}
}
三、反向迭代器
反向迭代器是一种反向遍历容器的迭代器:从最后一个元素到第一个元素遍历容器。反向迭代器将自增/自减的含义反过来了:对于反向迭代器,++运算将訪问前一个元素,而—元素将訪问下一个元素。
容器定义的rbegin和rend成员,分别返回指向容器尾元素和首元素前一位置的反向迭代器。
//逆序输出vector对象中的元素
vector<int> iVec;
for (vector<int>::size_type i = 0; i != 10; ++i)
{
iVec.push_back(i);
} for (vector<int>::reverse_iterator r_iter = iVec.rbegin(); r_iter != iVec.rend(); ++r_iter)
{
cout << *r_iter << endl;
}
尽管颠倒自增和自减这两个操作符的意义似乎easy使人迷惑,可是它让程序猿能够透明地向前或向后处理容器。比如,为了以降序排列vector,仅仅需向sort传递一对反向迭代器:
sort(iVec.rbegin(),iVec.rend());
当然,假设不嫌麻烦,也能够这样:
sort(iVec.begin(),iVec.end(),cmp);
cmp函数定义例如以下:
bool cmp(int a,int b)
{
return a > b;
}
1、反向迭代器须要使用自减操作符
从一个既支持--也支持++的迭代器就能够定义反向迭代器。毕竟,反向迭代器的目的是移动迭代器反向遍历序列。标准容器上的迭代器既支持自增运算,也支持自减运算。可是,流迭代器却不然,因为不能反向遍历流,因此流迭代器不能创建反向迭代器。
2、反向迭代器与其它迭代器之间的关系
输出 string对象line中的第一个单词。使用find可非常easy地实现这个任务:
string::iterator comma = find(line.begin(),line.end(),',');
cout << string(line.begin(),comma) << endl;
假设要输出列表中的最后一个单词,能够使用反向迭代器:
string::reverse_iterator rcomma = find(line.rbegin(),line.rend(),',');
//以下的输出结果可能不能满足你的要求...
cout << string(line.rbegin(),rcomma) << endl;
假设line中的最后一个单词是Primer,那么,输出的结果为:remirP【是不是非常不可思议?】见下图:
使用反向迭代器时,以逆序从后向前处理string对象。为了得到正确的输出,必须将反向迭代器line.rbegin()和 rcomma转换为从前向后移动的普通迭代器。事实上不是必需转换line.rbegin(),因为我们知道转换的结果必然是line.end()。仅仅需调用全部反向迭代器类型都提供的成员函数base转换rcomma就可以:
cout << string(rcomma.base(),line.end()) << endl;
正如 line_rbegin()和line.end()一样,rcomma和rcomma.base()也指向不同的元素。从技术上来说,设计普通迭代器与反向迭代器之间的关系是为了适应左闭合范围这个性质的,所以,[line.rbegin(),rcomma) 和[rcomma.base(),line.end()) 标记的是line中的同样元素。
使用普通的迭代器对反向迭代器进行初始化或赋值时,所得到的迭代器并非指向原迭代器所指向的元素!!!