根据流类体系,我们可以看出基本的流类是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的扩充机制;事件处理的回调函数机制;操控器;