首先回忆一下,我们已经分析过的类:基本的流类完成:流状态信息,格式化信息,其中一些不能用掩码表示,需要特定的数据成员,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
};