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

  根据流类体系,我们可以看出基本的流类是ios_base和basic_ios<>。

 首先是ios_base是流类始祖,先分析ios_base,ios_base中储存的是不依赖于读写的数据的类型的基本信息,如格式化信息、异常状态、事件回调函数等。

首先看一下数据成员:

        iostate _mystate;//stream state
	iostate _except_;//except mask
	fmtflags _fmtfl;//field flags
	streamsize _prec;//field precision
	streamsize _wide;//field width
	locale* _ploc;//pointer to locale
	//后面还有两个数据成员_arr和_calls
       _Iosarray* _arr;//ponter to first node of long/pointer
       static int _index;
       _Fnarray* _calls;//pointer to first node of call list

先不用管后面三个数据成员,我们可以看到,里面存储的是流的状态_mystate,异常标志_except_,格式信息_fmtfl,精度, 宽度还有locale对象。

首先是流的状态_mysate,标志着i/o操作是否成功,并且能够指出不成功的原因。

	operator void*() const
	{//test if any stream operation has failed
		return (fail()?0:(void*)this);
	}
	bool operator!() const
	{//test if no stream operation has failed
		return(fail());
	}
	void clear(iostate _state,bool _reraise)
	{
		_mystate=static_cast<iostate>(_state&_Statmask);
		if((_mystate&_except_)==0)
			;
		else if(_reraise)
			throw;
		else if (_mystate& _except_&badbit)
			throw failure("ios_base::badbit set");
		else if(_mystate& _except_&failbit)
			throw failure("ios_base::fail set");
		else
			throw failure("ios_base::eof set");
	}
	void clear(io_state _state)
	{//set state to argument.old-style
			clear(static_cast<iostate>(_state));
	}
	iostate rdstate() const
	{//return stream state
		return (_mystate);
	}
	//setstate就不写了,跟basic_ios中一样
	
	bool good() const
	{
		return (rdstate()==goodbit);
	}
	bool eof() const
	{
		return ((int)rdstate()&(int)eofbit);
	}
	bool fail() const
	{
		return (((int)rdstate()&((int)badbit|(int)failbit))!=0);
	}



上述就是与_mystate有关的函数,我想重点指出的技术是:operator void*()和operator!()这两个函数可以使我们可以直接使用

if(cin)或if(!cin)之类的语句判断流的状态,但是我们观察代码得知,内部调用的是fail()函数跟eof()函数无关,除非在eof之

后设立failbit标志,然后通过fail()函数来检测。


下面是异常机制_except_,注意他的类型也是iostate,它存储的是能引发异常的标志,若其值为badbit,那么只有流状态是bad的时候才会抛出异常:

	//内含类,用于异常机制
	class failure :public system_error
	{//base of all iostreams exceptions
	public:
		explicit failure(const string& _message,const error_code& _errcode=make_error_code(io_errc::stream))
			:system_error(_errcode,_message)
		{
		}
		explicit failure(const char* _message,const error_code& _errcode=make_error_code(io_errc::stream))
			:system_error(_errcode,_message)
		{
		}
	};

	iostate exceptions() const
	{
		return (_except_);
	}
	void exceptions(iostate _newexcept)
	{
		_except_=(iostate)( (int)_newexcept &(int)_Statmask);
		clear(_mystate);
	}
	void exceptions(io_state _state)
	{
		exceptions((iostate)_state);
	}

这就是与异常有关的代码。

下面是格式信息_fmtfl。格式信息就是确定格式:如是八进制还是十进制,bool值时数字还是文字显示,是否大写。浮点数的表示法等等。

具体的常用标志符如下:

bool值的格式,是数字表示还是文字表示;位置调整:left对齐还是right对齐;正数是否需要+号;字符手否需要大写;数值的进制显示,以及是否在数值中表明进制;浮点数是用小数计数法还是科学计数法等等;


然而对于格式化信息,远不止这么多。例如数值显示的精度,数值的填充字符,字段宽度等。这些信息不是做选择题而是做填空题,所以单纯的掩码选择无法满足,我们使用_prec,_width存储精度和宽度。对于填充字符,它与字符特性有关,所以不包含在基类中,基类仅仅包含与字符特性无关的信息;

	fmtflags flags() const
	{
		return (_fmtfl);
	}
	fmtflags flags(fmtflags _newfmtflags)
	{
		fmtflags _oldfmtflags=_fmtfl;
		_fmtfl=(fmtflags)( (int)_newfmtflags &(int)_Fmtmask);
		return (_oldfmtflags);
	}

对于格式化信息的设置,通过flags重新设立;或者通过setf/unsetf函数添加或删除格式标志。

当然也有专门的设立width和精度的函数,就不附代码了。

还有一个重要的locale成员,因为我们把大部分的工作都交给了locale来做,后面会说明这一点。

除了上述信息之外,ios_base类中还定义了两种机制:

iword/pword机制。iword和pword是两个函数,以特定的索引(int)为参数,返回long&或void*;这个机制主要是为了扩展我们的格式化信息,难道我们对于格式化的要求仅仅是精度?宽度?填充符?上述格式很重要,能满足我们大部分需求,但是对于某些特定的类型,例如自定义类型,我们可以定义属于自己的格式化,例如:对于复数,我们使用i还是j作为复数的标志?

	struct _Iosarray
	{//list element for open-ended sparse array of longs/pointers
		_Iosarray(int _idx,_Iosarray* _link)
			:_next(_link),_index(_idx),_long(0),_vp(0)
		{
		}
		_Iosarray* _next;//pointer to next node
		int _index;//index of this node;
		long _long;//store long value
		void *_vp;//store pointer value
	};
	//ios_base的数据成员
	_Iosarray* _arr;//ponter to first node of long/pointer
	static int _index;

	_Iosarray& _Findarr(int _idx)
	{//locale or make a variable array element
		_Iosarray* _ptr1;
		_Iosarray* _ptr2;
		for(_ptr1=_arr,_ptr2=0;_ptr1!=0;_ptr1=_ptr1->_next)
			if(_ptr1->_index==_idx)
				return (*_ptr1);
			else if(_ptr2==0 && _ptr1->_long==0&&_ptr1->_vp==0)
				_ptr2=_ptr1;

		if(_ptr2!=0){
			_ptr2->_index=_idx;
			return(*_ptr2);
		}
		_arr=new  _Iosarray(_idx,_arr);//产生新元素
		return(*_arr);
	}
public:
	static int xalloc()
	{//allocate new iword/pword index
		return (_index++);//
	}
	long& iword(int _idx)
	{//return reference to long element
		return (_Findarr(_idx)._long);
	}
	void*& pword(int _idx)
	{//return reference to pointer element
		return(_Findarr(_idx)._vp);
	}

首先,ios_base维持一个Iosarry*类型的数据成员_arr,它可以形成链表,也就是数量不受限制,对于Iosarray类型,代码中已明确给出,不过我觉得应用union会更好一些,毕竟我们要求是long或void*其中之一。

同时类中有静态成员:_index,来表明_arr的元素个数。

此机制的使用方法如下:解决的问题是:对于复数,我们使用i还是j作为复数的标志

首先使用xalloc函数,分配一个唯一的id,然后,我们我们调用iword或pword;具体如下:

//complex是复数类,其数据成员有real和imag
//首先调用xalloc来“分配”空间,实际是使_index加1,为什么不分配一个Iosarray对象呢,我估计可以是C++的设计哲学:绝不做
//不需要的事情,我们调用xalloc并不一定会使用它,只有真正使用时才分配空间--在_Findarr()函数中
static const int iword_index=ios_base::xalloc();
//我们假设流有成员函数seti(),设立标志符是i,unseti(),设立标识符是j
void seti()
{
	iword(iword_index)=true;
}
void unseti()
{
	iword(iword_index)=false;
}
//实际上这两个函数要起得效果完全可以用操控器实现,操控器后面分析
std::ostream operator<<(ostream& strm,const complex& _val)
{
	if(iword(iword_index))
		strm<<_val.real<<'+'<<_val.imag<<'i';
	else
		strm<<_val.real<<'+'<<_val.imag<<'j';
	return strm;
}

//使用:
strm.seti();
strm<<_val;

除上述机制外,ios_base还定义的回调函数机制,主要支持两种行为:必要时执行深度拷贝;销毁stream时连带销毁某个对象;

	enum event//每个事件发生都会调用对应的回调函数
	{//constant for ios events
		erase_event,imbue_event,copyfmt_event};
        typedef void(* event_callback)(event,ios_base&,int);

 //回调函数机制
private:
	struct _Fnarray
	{//lsit element for open-ended sparse array of event handle
		_Fnarray(int _idx,event_callback _pnew,_Fnarray* _link)
			:_next(_link),_index(_idx),_pfn(_pnew)
		{
		}
		_Fnarray* _next;//pointer to next node
		int _index;//index of this node
		event_callback _pfn;//pointer to event handler
	};
	//ios_base的数据成员
	_Fnarray* _calls;//pointer to first node of call list
	
	void _Callfns(event _ev)
	{//call all event handlers,reporting event
		for(_Fnarray* _pfa=_calls;_pfa!=0;_pfa=_pfa->_next)
			(*_pfa->_pfn)(_ev,*this,_pfa->_index);
	}
public:
	//注册一个回调函数
	void register_callback(event_callback _pfn,int _idx)
	{
		_calls=new _Fnarray(_idx,_pfn,_calls);
	}

ios_base维持一个_Fnarray*的数据成员_calls;其原理和上述的_arr一样;

具体的应用,以ios_base中copyfmt为例:


	ios_base& copyfmt(const ios_base& _other)
	{//copy format stuff
		_Tidy();
		*_ploc=*_other._ploc;
		_fmtfl=_other._fmtfl;
		_prec=_other._prec;
		_wide=_other._wide;
		_Iosarray* _ptr=_other._arr;
		for(_arr=0;_ptr!=0;_ptr=_ptr->_next){
			if(_ptr->_long!=0 ||_ptr->_vp!=0){
				//copy over nonzero array value
				iword(_ptr->_index)=_ptr->_long;
				pword(_ptr->_index)=_ptr->_vp;
			}
		}
		for(_Fnarray* _pfa=_other._calls;_pfa!=0;_pfa=_pfa->_next){
			register_callback(_pfa->_pfn,_pfa->_index);
		}
		_Callfns(copyfmt_event);
		exceptions(_other._except_);
		return(*this);
	}

_Callfns(copyfmt_event)被调用,用于处理与copyfmt_event有关的问题;

还有几个重要的函数或者成员:

class Init
	{//controller for standard-stream initialization
	public:
		Init()
		{//在构造时初始化
			_Init_ctor(this);
		}
		~Init()
		{//析构前刷新
			_Init_dtor(this);
		}
	private:
		static void _Init_ctor(Init* );
		static void _Init_dtor(Init*);
		static int _Init_cnt;//net ctor count
		static int& Init_cnt_func();
	};
	virtual ~ios_base()
	{
		_Ios_base_dtor(this);
	}
	static void _Addstd(ios_base*);//add standard stream
	size_t _stdstr;//if>0 index of standaed stream  to suppress destruction
protected:
	ios_base()
	{
	}
	void _Init()
	{//initialize a new ios_base
		_ploc=0;
		_stdstr=0;
		_except_=0;
		_fmtfl=(fmtflags)(skipws|dec);
		_prec=6;
		_wide=0;
		_arr=0;
		_calls=0;
		clear(goodbit);
		_ploc= new locale;
	}
private:
	void _Tidy()
	{//discard storage for an ios_base
		_Callfns(erase_event);
		_Iosarray* _ptr1;
		_Iosarray* _ptr2;
		for(_ptr1=_arr;_ptr1!=0;_ptr1=_ptr2){
			_ptr2-_ptr1->_next;
			delete _ptr1;
		}
		_arr=0;

		_Fnarray* _pfa1;
		_Fnarray* _pfa2;
		for(_pfa1=_calls;_pfa1!=0;_pfa1=_pfa2){
			_pfa2=_pfa1->_next;
			delete _pfa1;
		}
		_calls=0;
	}
	static void _Ios_base_dtor(ios_base*);

下面说明操控器的概念:

我们要想设立格式,例如使bool值以文字表示,那么需要如下语句:

bool b;   strm.setf(boolalpha);    strm<<b;

然而我们喜欢的格式是:bool b; strm<<boolalpha<<b;

为了引进我们喜欢的简便形式,而非掩码操作,引入了操控器;

首先定义一系列函数,跟掩码有着差不多的名字,如:

//下面定义各种操控器,manipulators
inline ios_base& boolapha(ios_base& _losbase)
{//set boolpha
	_losbase.setf(ios_base::boolalpha);
	return(_losbase);
}
inline ios_base& dec(ios_base& _losbase)
{//set basefield to dec
	_losbase.setf(ios_base::dec,ios_base::basefield);
}
inline ios_base& defaultfloat(ios_base& _losbase)
{//clear floatfield
	_losbase.unsetf(ios_base::floatfield);
	return (_losbase);
}
inline ios_base& fixed(ios_base& _losbase)
{//set floatfield to fixed
	_losbase.setf(ios_base::fixed,ios_base::floatfield);
	return (_losbase);
}
inline ios_base&  hex(ios_base& _losbase)
{//set basefield to hex
	_losbase.setf(ios_base::hex,ios_base::basefield);
	return (_losbase);
}
inline ios_base& left(ios_base& _losbase)
{//set adjustfield to left
	_losbase.setf(ios_base::left,ios_base::adjustfield);
	return (_losbase);
}
inline ios_base& noboolalpha(ios_base& _losbase)
{//clear boolapha
	_losbase.unsetf(ios_base::boolalpha);
	return (_losbase);
}
 //............各种操控器,此处不一一列举

然后定义operator<<重载,它有着如下形式:

ios_base& operator<<(ios_base& strm,ios_base&(* _manip)(ios_base&))
{//左参数是流,右参数是函数指针(操控器)
	_manip(strm);
}

于是,我们就可以使用简便形式了。


好,ios_base的知识点理完了。首先,ios_base设计出来是为了流的存储基本特性的,流的状态,流中格式化信息;

然后实现的时候采用了很多实现手段:上述的各个函数,iword/pword的扩充机制;事件处理的回调函数机制;操控器;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值