首先回忆一下,我们已经分析过的类:基本的流类完成:流状态信息,格式化信息,其中一些不能用掩码表示,需要特定的数据成员,locale对象,流缓冲类指针,耦合的ostream;基本的流缓冲类完成:六个指针的存储。
这个我们分析的是basic_ostream<>,真正完整的流类。在分析之前,我们需要明确的是,许多的工作都是由locale对象里的facet完成的。
首先是类中会用到的类型声明:
typedef basic_ostream<_Elem,_Traits> _Myt; typedef basic_ios<_Elem,_Traits> _Myios; typedef basic_streambuf<_Elem, _Traits> _Mysb; typedef ostreambuf_iterator<_Elem, _Traits> _Iter; typedef num_put<_Elem, _Iter> _Nput; typedef typename _Traits::int_type int_type; typedef typename _Traits::pos_type pos_type; typedef typename _Traits::off_type off_type;
值得庆幸的是:这个类中没有增加新的数据成员,主要是完成我们平常使用的一些接口函数的实现,其中最有代表性的就是:operator<<。
首先,类引入了哨兵机制:
//哨兵 class _Sentry_base {//store thread lock and reference to out stream public: _Sentry_base(_Myt& _ostr) :_myostr(_ostr) {//look the stream buffer if(_myostr.rdbuf()!=0) _myodtr.rdbuf()->_Lock(); } ~_Sentry_base() {//destroy after unlocking if(_myostr.rdbuf()!=0) _myostr.edbuf()->_Unlock(); } _Myt& _myostr; private: _Sentry_base& operator=(const _Sentry_base&); }; class sentry :public _Sentry_base {//stores thread lock and state of stream public: explicit sentry(_Myt& _ostr) :_Sentry_base(_ostr) { if(_ostr.good() && _ostr.tie()!=0) _ostr.tie()->flush(); _ok=_ostr.good();//store test only after flush tie } ~sentry() {//destroy the object if(!uncaught_exception()) this->_myostr._Osfx(); } private: bool _ok;//true if stream state okay at construction sentry(const sentry&);//not defined sentry& operator=(const sentry&); }; bool opfx() {//test stream state and flush tie stream if(ios_base::good() && _Myios::tie()!=0) _Myios::tie()->flush(); return (ios_base::good()); } void osfx() {//perform any wrapup _Osfx(); } void _Osfx() { if(ios_base::flags() & ios_base::unitbuf)//当缓冲区不为空时,输出处理 flush(); }
这个哨兵,主要是完成三个工作:状态测试--保证流在可工作状态;保证线程安全;构造和销毁时是否或者说是如何刷新缓冲区的操作。它的使用范围很广,以至于我们大多数情况下觉得它有些浪费,尤其是在扩展自己的operator<<时。
下面的函数是为了能够使用strm<<oct<<val;此类语句,使操控器变得好用的关键:
_Myt& operator<<(_Myt& (*_pfn)(_Myt&)) {//call basic_ostream manipulator return((*_pfn)(*this)); } _Myt& operator<<(_Myios& (*_pfn)(_Myt&)) { return((*_pfn)(*this)); } _Myt& operator<<(_Myios& (*_pfn)(_Myios&)) { (*_pfn)(*(_Myios*)this); return *this; }
怎么把一个数据写到流中?
typedef bool _Bool; _Myt& operator<<(_Bool _val) { ios_base::iostate _state=ios_base::goodbit; const sentry _ok(*this); if(_ok){ //state okay use facet to insert const _Nput& _nput_fac=_USE(ios_base::getloc(),_Nput); if(_nput_fac.put(_Iter(_Myios::rdbuf()),*this,_Myios::fill(),_val).failed()) _state|=ios_base::badbit; }//put(to,fmt,fill,value),to是output迭代器,fmt是格式信息,fill是填充字符,put返回一个迭代器指向下一个位置 _Myios::setstate(_state); return(*this); } _Myt& operator<<(short _val) { ios_base::iostate _state=ios_base::goodbit; const sentry _ok(*this); if(_ok){ const _Nput& _nput_fac=_USE(ios_base::getloc(),_Nput); ios_base::fmtflags _bfl=ios_base::flags()&ios_base::basefield; bool _tmp=(_bfl==ios_base::oct||_bfl==ios_base::hex)?(long)(unsigned short)_val,(long)_val; if(_nput_fac.put(_Iter(_Myios::rdbuf()),*this,_Myios::fill(),_tmp).failed()) _state|=ios_base::badbit; } _Myios::setstate(_state); return(*this); }
其基本操作流程:首先构造sentry对象,如果sentry对象时“良好”的,那么开始使用locale中相应的facet将数据写到缓冲区内,如果失败则将流状态改为bad,最后更新流状态;注意,facet完成了绝大部分最困难的工作,完成数据的格式化,写到缓冲区,缓冲区的行为由具体的缓冲类决定。除了bool,short型之外,还有int,double。。。的重载类型,此处不赘述。不过下面的这个重载版本值得注意:
//其余的各个类型就不写了 _Myt& operator<<(_Mysb* _strbuf) { ios_base::iostate _state=ios_base::goodbit; bool _copied=false; const sentry _ok(*this); if(_ok && _strbuf!=0){ for(int_type _meta=_Traits::eof();;_copied=true){ //extract another character from stream buffer _meta=_Traits::eq_int_type(_Traits::eof(),_meta)?_strbuf->sgetc():_strbuf->snextc(); if(_Traits::eq_int_type(_Traits::eof(),_meta)) break; if(_Traits::eq_int_type(_Traits::eof(),_Myios::rdbuf()->sputc(_Traits::to_char_type(_meta)))) { _state|=ios_base::badbit; break; } } } ios_base::width(0); _Myios::setstate(_strbuf==0?ios_base::badbit :(!_copied?_state|ios_base::failbit:_state); return (*this); }
这个重载版本对于效率上的提升很重要,我们可以使用如下形式:cout<<cin.rdbuf();
剩下的函数就是对缓冲类函数的重包装,例如:put,write,flush等等都是将工作转交给缓冲类。
下面的函数实现读取一个字符,不同于bool,int型等的格式化的复杂性,对于读取资格字符,我们不需要facet对象,可以直接完成:
template<class _Elem,class _Traits> inline basic_ostream<_Elem,_Traits>& operator<<( basic_ostream<_Elem,_Traits>& _ostr,_Elem _ch) {//insert an character typedef basic_ostream<_Elem,_Traits> _Myos; ios_base::iostate _state=ios_base::goodbit; const typename _Myos::sentry _ok(_ostr); if(_ok){ streamsize _pad=_ostr.width()<1?0:_ostr.width()-1; if((_ostr.flags()&ios_base:;adjustfield)!=ios_base::left) for(;_state==ios_base::goodbit && 0<_pad;--pad) //pad on left if(_Traits::eq_int_type(_Traits::eof(),_ostr.rdbuf()->sputc(_ostr.fill()))) _state|=ios_base::badbit; if(_state==ios_base::goodbit && _Traits::eq_int_type(_Traits::eof(),_ostr.rdbuf()->sputc(_ch))) _state|=ios_base::badbit; for (; _state == ios_base::goodbit && 0 < _pad;--_pad) // pad on right if (_Traits::eq_int_type(_Traits::eof(), _ostr.rdbuf()->sputc(_ostr.fill()))) _state |= ios_base::badbit; } _ostr.width(0); _ostr.setstate(_state); return _ostr; }
主要是完成左对齐还是右对齐,字段宽度及填充。对于_Elem为char或wchar版本的特化版本,此处不赘述。
最后附一个endl操控器的代码:
template<class _Elem,class _Traits> inline basic_ostream<_Elem, _Traits>& endl(basic_ostream<_Elem, _Traits>& _ostr) { // insert newline and flush stream _ostr.put(_ostr.widen('\n')); _ostr.flush(); return (_ostr); }
其基本的ios_base& function(ios_base&)的形式没变,不过不同于以前我们定义过的操控器,此操控器不只是设置格式化信息,而是实实在在的操作。
下面是ostringstream类的源码,你会发现这个类做的工作只是包装,全部的工作已经由原先的基类完成,我们对于扩展自己的流类也会如此简单:
template<class _Elem, class _Traits, class _Alloc> class basic_ostringstream : public basic_ostream<_Elem, _Traits> { // output stream associated with a character array public: typedef basic_ostringstream<_Elem, _Traits, _Alloc> _Myt; typedef basic_ostream<_Elem, _Traits> _Mybase; typedef _Alloc allocator_type; typedef basic_stringbuf<_Elem, _Traits, _Alloc> _Mysb; typedef basic_string<_Elem, _Traits, _Alloc> _Mystr; explicit basic_ostringstream(ios_base::openmode _Mode = ios_base::out) : _Mybase(&_Stringbuffer), _Stringbuffer(_Mode | ios_base::out) { // construct empty writable character buffer } explicit basic_ostringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::out) : _Mybase(&_Stringbuffer), _Stringbuffer(_Str, _Mode | ios_base::out) { // construct writable character buffer from NTCS } basic_ostringstream(_Myt&& _Right) : _Mybase(&_Stringbuffer) { // construct by moving _Right _Assign_rv(_STD forward<_Myt>(_Right)); } _Myt& operator=(_Myt&& _Right) { // move from _Right _Assign_rv(_STD forward<_Myt>(_Right)); return (*this); } void _Assign_rv(_Myt&& _Right) { // assign by moving _Right if (this != &_Right) { // different, worth moving _Stringbuffer.str(_Mystr()); this->swap(_Right); } } void swap(_Myt& _Right) { // swap with _Right if (this != &_Right) { // different, swap base and buffer _Mybase::swap(_Right); _Stringbuffer.swap(_Right._Stringbuffer); } } private: basic_ostringstream(const _Myt& _Right); // not defined _Myt& operator=(const _Myt&); // not defined public: virtual ~basic_ostringstream() _NOEXCEPT { // destroy the object } _Mysb *rdbuf() const { // return pointer to buffer return ((_Mysb *)&_Stringbuffer); } _Mystr str() const { // return string copy of character array return (_Stringbuffer.str()); } void str(const _Mystr& _Newstr) { // replace character array from string _Stringbuffer.str(_Newstr); } private: _Mysb _Stringbuffer; // the string buffer };