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

来源:互联网 发布:百度图片json接口 编辑:程序博客网 时间:2024/06/05 01:00

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

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

首先看一下数据成员:

        iostate _mystate;//stream stateiostate _except_;//except maskfmtflags _fmtfl;//field flagsstreamsize _prec;//field precisionstreamsize _wide;//field widthlocale* _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 failedreturn (fail()?0:(void*)this);}bool operator!() const{//test if no stream operation has failedreturn(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");elsethrow failure("ios_base::eof set");}void clear(io_state _state){//set state to argument.old-styleclear(static_cast<iostate>(_state));}iostate rdstate() const{//return stream statereturn (_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 exceptionspublic: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 nodeint _index;//index of this node;long _long;//store long valuevoid *_vp;//store pointer value};//ios_base的数据成员_Iosarray* _arr;//ponter to first node of long/pointerstatic 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 indexreturn (_index++);//}long& iword(int _idx){//return reference to long elementreturn (_Findarr(_idx)._long);}void*& pword(int _idx){//return reference to pointer elementreturn(_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(),设立标识符是jvoid 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';elsestrm<<_val.real<<'+'<<_val.imag<<'j';return strm;}//使用:strm.seti();strm<<_val;

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

enum event//每个事件发生都会调用对应的回调函数{//constant for ios eventserase_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 nodeint _index;//index of this nodeevent_callback _pfn;//pointer to event handler};//ios_base的数据成员_Fnarray* _calls;//pointer to first node of call listvoid _Callfns(event _ev){//call all event handlers,reporting eventfor(_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 valueiword(_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 initializationpublic: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 countstatic int& Init_cnt_func();};virtual ~ios_base(){_Ios_base_dtor(this);}static void _Addstd(ios_base*);//add standard streamsize_t _stdstr;//if>0 index of standaed stream  to suppress destructionprotected: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;

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

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

//下面定义各种操控器,manipulatorsinline 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的扩充机制;事件处理的回调函数机制;操控器;

原创粉丝点击