现在的位置: 首页 > 综合 > 正文

C++的I/O(vc版)(六)

2014年02月20日 ⁄ 综合 ⁄ 共 6802字 ⁄ 字号 评论关闭

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

抱歉!评论已关闭.