短期代码阅读主要基于SGI的STL,测试环境则是GCC_4.8.3_STL和VS_STL。暂时不去配置BOOST等库STL细节。待续
1、istream_iterator 输入流迭代器
1)没有operator=操作,因为只读,不可写,所以编译不支持。迭代器句柄保存当前已读取到的数据。
_GLIBCXX_CONSTEXPR istream_iterator() : _M_stream(0), _M_value(), _M_ok(false) {} /// Construct start of input stream iterator. istream_iterator(istream_type& __s) : _M_stream(&__s) { _M_read(); }
在对象声明即初始化的世界,三个版本的构造函数代码是类似的,有个小坑:除了初始化的默认参数是不一样的之外,在使用绑定至正确输入对象时,自动调用了读取操作。
2)有个读取操作为protected,向外提供,使用句柄的形式听过数据访问操作.。在SGI中的read操作和GCC:_M_read实现比较类似,都是单独定义一个状态控制量,根据控制量去确定是否进行输入迭代,正确情况下会调用输入对象的输入符操作。重点:当出现错误的情况并不会修正迭代器指向,这点和VS:_Getval 的实现不一样。
void _M_read() { _M_ok = (_M_stream && *_M_stream) ? true : false; if (_M_ok) { *_M_stream >> _M_value; _M_ok = *_M_stream ? true : false; } }
而在VS:_Getval 中,实现由些差别:
void _Getval() { // get a _Ty value if possible if (_Myistr != 0 && !(*_Myistr >> _Myval)) _Myistr = 0; }
VS出现输入错误终止的情况下会把当前迭代器的值置为0,也就是说不在绑定至任何输入流,具有不可重复性操作。基于这点,我们无法再多平台上根据输入流的状态进行容错处理,至少vs是不支持的,在1:处理完输入流的状态后得2:重新设置迭代器的状态才行继续输入(SGI和GCC则没有步骤2可以继续使用输入迭代器)。
3、迭代器进行迭代时,
operator++
完成读取数据的操作。外界使用其接口读取数据则是另外一个接口:解引用取值操作。也就是时候但迭代失败时,保留的是上一个数据
istream_iterator& operator++() { __glibcxx_requires_cond(_M_ok, _M_message(__gnu_debug::__msg_inc_istream) ._M_iterator(*this)); _M_read(); return *this; }
正确的使用方式如下:
std::istream_iterator<int> in_it(cin),eos;//输入输出的迭代器结束符和文件等结束符不一样,是迭代器的判定不一样,所以定义一个空迭代器判定是否为终止状态 while (!in_it._Equal(eos)) { cout<<*in_it; cout<<endl; ++in_it; }
关于结束判定的这点,因为源码实现有点不一样,GCC实现如下:在控制量
bool _M_equal(const istream_iterator& __x) const { return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); }
而SGI port的则增加输入流状态处理,如首先校验两个迭代器是否完成输入操作,没有的话继续执行(这个估计得看操作系统的操作,一旦IO设备未置位读取完成,就继续执行读取,不出现多次读取的处理是交由操作系统或相关api去处理,而不是在这里控制。)是:
bool _M_equal(const _Self& __x) const { if (!_M_read_done) { _M_read(); } if (!__x._M_read_done) { __x._M_read(); } return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); }
当然SGI的代码则是在operator==操作符中实现
template <class _Tp, class _Distance> inline bool operator==(const istream_iterator<_Tp, _Distance>& __x, const istream_iterator<_Tp, _Distance>& __y) { return (__x._M_stream == __y._M_stream && __x._M_end_marker == __y._M_end_marker) || __x._M_end_marker == false && __y._M_end_marker == false; }
最后来到比较坑的代码中:
SGI:
istream_iterator() : _M_stream(&cin), _M_end_marker(false) {} istream_iterator(istream& __s) : _M_stream(&__s) { _M_read(); }
SGI PORT:
istream_iterator() : _M_stream(0), _M_ok(false), _M_read_done(true) {} istream_iterator(istream_type& __s) : _M_stream(&__s), _M_ok(false), _M_read_done(false) {} // 重点,没有进行首次读取
reference operator*() const { //采用惰性处理,蛮好的,适合理解 if (!_M_read_done) { _M_read(); } return _M_value; }
GCC:
_GLIBCXX_CONSTEXPR istream_iterator() : _M_stream(0), _M_value(), _M_ok(false) {} /// Construct start of input stream iterator. istream_iterator(istream_type& __s) : _M_stream(&__s) { _M_read(); }
VS:
istream_iterator() : _Myistr(0) { // construct singular iterator } istream_iterator(istream_type& _Istr) : _Myistr(&_Istr) { // construct with input stream _Getval(); }
实际上真正按照标准实现的是STL port,可是其他家成为事实的标准,都在有绑定状态优先执行首次读取。不过不用担心,所有的算法库都是先读取首操作采取执行operator++操作,实际上的数据处理不对出现说丢失状态。
无论哪个版本,最后表现的结果都是一致的(除效率外),基于这点,在基本原理实现上,我优先阅读SGI和GCC的源码,偶尔辅助VS的实现。
ostream_iterator 的源码思想也类似,重载了operator=,不提供指针偏移等操作(迭代操作在于原位),就不继续贴代码啦。
都是定义一个空迭代作为哨兵。
std::istream_iterator<int> in_it(cin),eos; while (!(in_it==eos)) { cout<<*in_it; cout<<endl; ++in_it; }