上一节我们已经了解了虚拟序列了,现在让我们看看虚拟序列其他的应用吧。
看看书上所提及的输出流迭代器
#ifndef OSTREAM_ITERATOT_H_
#define OSTREAM_ITERATOT_H_
#include <iostream>
#include <string>
template <typename T>
class Ostream_iterator {
public:
Ostream_iterator(std::ostream&, const std::string&);
Ostream_iterator& operator++();
Ostream_iterator& operator++(int);
Ostream_iterator& operator*();
Ostream_iterator& operator=(const T&);
private:
std::ostream* os;//这里只能是指针型
std::string str;
};
#include "Ostream_iterator.cpp"
#endif
这里私有成员之所以是ostream*型的指针,而不直接是ostream的对象是因为ostream的对象是不允许复制的。若改为ostream os,则在初始化的时候不可避免地进行了复制。
实现部分挺简单的
#include "Ostream_iterator.h"
template <typename T>
Ostream_iterator<T>::Ostream_iterator(std::ostream& out, const std::string& s):
os(&out), str(s) { }
template <typename T>
Ostream_iterator<T>& Ostream_iterator<T>::operator++()
{
return *this;
}
template <typename T>
Ostream_iterator<T>& Ostream_iterator<T>::operator++(int)
{
return *this;
}
template <typename T>
Ostream_iterator<T>& Ostream_iterator<T>::operator*()
{
return *this;
}
template <typename T>
Ostream_iterator<T>& Ostream_iterator<T>::operator=(const T& type)
{
*os << type << str;
return *this;
}
这里输出是由对os赋值而触发的,对os赋值相当于将x打印到cout上。而自增运算可以实现为空操作,目的只要让我们设计的类看起来更像迭代器而已,并且满足输出迭代器概念上的要求。
接下来我们看看输入流的迭代器。相比于输出流迭代器,输入流迭代器显然更为复杂。
1. 首先,不同于Ostream_iterator,我们设计的Istream_iterator必须支持比较操作才能判断是否到达了文件尾部。
2. Istream_iterator必须可以进行尝试性的读取,因为需要判断是否已经到达文件尾部。(即判断输入是否有效)
接下来看看我们的策略。我们将设计成每个Istream_iterator对象将有一个只能容纳一个元素的缓冲区和一个表明缓冲区是否已满的标志(full)。引入缓冲区已满的标志,使我
们可以将每个输入分隔开。想想每当读入一个值时,我们便将full标记为1,表明缓冲区已满。而operator++又使full恢复为0。如此一来的效果便是我们可以遍历虚拟的输入序
列。
那么我们何时遍历结束了?显然是当我们不再输入元素。当我们不再输入元素时,我们往往会按下回车键。所以又可以说当我们读入一个坏值便是输入序列的结束。如此一来,
便还需要一个标志着序列结束的标志,也即文件尾(eof)。
经过我们这样的分析,那么书本上的那个问题也就不是文图了:full和eof是否能够同时为true?显然不能,根据我们的设计,只有读入坏值时eof才为1。
理论工作全部就位,接下来就是实现了。
#ifndef ISTREAM_ITERATOR_H_
#define ISTREAM_ITERATOR_H_
#include <iostream>
template <typename T> class Istream_iterator;
template <typename T> int operator==(Istream_iterator<T>&, Istream_iterator<T>&);
template <typename T> int operator!=(Istream_iterator<T>&, Istream_iterator<T>&);
template <typename T>
class Istream_iterator {
friend int operator== <T> (Istream_iterator<T>&, Istream_iterator<T>&);
friend int operator!= <T> (Istream_iterator<T>&, Istream_iterator<T>&);
public:
Istream_iterator();
Istream_iterator(std::istream&);
Istream_iterator& operator++();
Istream_iterator operator++(int);
T operator*();
void fill();
private:
T buffer;
std::istream* strm;
int full;
int eof;
};
#include "Istream_iterator.cpp"
#endif
这里函数声明和类的组织之间有技巧,这里只提及一下不详细讨论。
#include "Istream_iterator.h"
template <typename T>
Istream_iterator<T>::Istream_iterator(std::istream& is):
strm(&is), full(0), eof(0) { }
template <typename T>
Istream_iterator<T>::Istream_iterator():
strm(0), full(0), eof(1) { }
template <typename T>
Istream_iterator<T>& Istream_iterator<T>::operator++()
{
full = 0;
return *this;
}
template <typename T>
Istream_iterator<T> Istream_iterator<T>::operator++(int)
{
Istream_iterator<T> r = *this;
full = 0;
return r;
}
template <typename T>
void Istream_iterator<T>::fill()
{
if (!full && !eof) {
if (*strm >> buffer)
full = 1;
else
eof = 1;
}
}
template <typename T>
T Istream_iterator<T>::operator*()
{
fill();
return buffer;
}
template <typename T>
int operator==(Istream_iterator<T>& p, Istream_iterator<T>& q)
{
if(p.eof && q.eof)
return 1;
if(!p.eof && !q.eof)
return &p == &q;
p.fill();
q.fill();
return p.eof == q.eof;
}
template <typename T>
int operator!=(Istream_iterator<T>& p, Istream_iterator<T>& q)
{
return !(p == q);
}
operator==比较难理解,只有当两个Istream_iterator是同一个对象或者都是处于文件尾部时才相等。而实际中这个比较的唯一用处就是判断我们是否到达文件尾。
看看主函数可以怎么写
int _tmain(int argc, _TCHAR* argv[])
{
Ostream_iterator<int> out(std::cout, "\n");
Istream_iterator<int> input(std::cin);
Istream_iterator<int> eof;
copy(input, eof, out);
return 0;
}
终于,完成了我们输入输出流迭代器。