chapter8.docx
《chapter8.docx》由会员分享,可在线阅读,更多相关《chapter8.docx(68页珍藏版)》请在冰豆网上搜索。
chapter8
第八章:
将“课程总结”§11“格式化输出”中的部分内容加入“chapter_8.doc”。
第八章输入输出流(input/outputstream)
8.1预定义类型的输入/输出
8.1.1基本情况及其优点
C++输入输出流的优点:
(一)重载运算符“<<”和“>>”能以函数重载的形式极大地扩大用途,在输入输出流中充分体现多态性。
C语言的输入/输出系统本来就灵活性大、功能比较完善。
但它有一个较大缺点:
无法处理众多的用户自定义数据类型(主要是类及其对象)。
例如,有一个结构类型exampl如下:
structexampl{
intj;
charstr[80];
}str_ex;
如欲输出此结构对象str_ex的两个成员的内容,因而笼统地使用如下输出语句printf,
printf(“%exampl”,str_ex);
则将会出现编译错误。
而C++的输出/输入系统则能很好地解决这个问题。
(二)类型安全(type-safe)
[例1]C语言输出语句中的类型错误(第一章已看过)
#include
voidmain()
{
inti=3;
doubled=4.4;
printf(“%d\t%f\n”,i,d);
}
运行结果:
34.400000对!
但如写错为:
printf(“%d\t%d\n”,i,d);
则编译时不出错,但运行结果错,为:
3-26214
但如写cout<
则得34.4,对!
始终不会出错!
(三)通过缓存增加功能。
(四)附带优点是书写方便以及显示中没有冗余字符,能自动略去尾数中的零(但如用户希望显示多余的零,也可以做到)。
C++的输入/输出系统是对流的操作,也即操作数据使其流向对象,或从对象流出。
什么是流?
流是从源头到目的的数据流动。
当键入字符时,字符从键盘流入程序中;当将数据写入磁盘文件中时,数据流动至磁盘上。
C++输入/输出流库是使用继承方法建立起来的一个输入/输出类库,它具有两个平行的基类,即streambuf类和ios类。
所有其它流类都是从它们直接或间接地派生出来的。
streambuf类用于提供物理设备的接口,它提供缓冲或处理流的通用方法。
它作为一个虚基类,具有类层次如下:
图8.1
ios类及其派生类用于为用户提供使用流类的接口。
它使用streambuf完成检查错误的格式化输入/输出操作,并支持对streambuf的缓冲区进行输入/输出时的格式化或非格式化转换。
ios类作为流库中的一个虚基类,派生出许多派生类,其主要层次如下:
图8.2
8.1.2预定义流(标准流)
预定义输出输入流涉及较多的头文件有四个:
ios.h,istream.h,ostream.h和iostream.h。
下面分别介绍。
8.1.2.1输出流
流输出运算符“<<”是在头文件ostream.h的classostream中定义的。
从图8.2可以看出,classostream是从classios中派生出来的。
因此下面先看一下用于定义classios的头文件ios.h。
先看ios.h:
/***
*ios.h-definitions/declarationsfortheiosclass.
***/
classios{
public:
enumio_state{goodbit=0x00,
eofbit=0x01,
failbit=0x02,
badbit=0x04};
enumopen_mode{in=0x01,
out=0x02,
ate=0x04,
app=0x08,
trunc=0x10,
nocreate=0x20,
noreplace=0x40,
binary=0x80};
enumseek_dir{beg=0,cur=1,end=2};
enum{skipws=0x0001,
left=0x0002,
right=0x0004,
internal=0x0008,
dec=0x0010,
oct=0x0020,
hex=0x0040,
showbase=0x0080,
showpoint=0x0100,
uppercase=0x0200,
showpos=0x0400,
scientific=0x0800,
fixed=0x1000,
unitbuf=0x2000,
stdio=0x4000};
staticconstlongbasefield;//dec|oct|hex
staticconstlongadjustfield;//left|right|internal
staticconstlongfloatfield;//scientific|fixed
ios(streambuf*);//differsfromANSI
virtual~ios();
inlinelongflags()const;
inlinelongflags(long_l);
inlinelongsetf(long_f,long_m);
inlinelongsetf(long_l);
inlinelongunsetf(long_l);
inlineintwidth()const;
inlineintwidth(int_i);
inlineostream*tie(ostream*_os);
inlineostream*tie()const;
inlinecharfill()const;
inlinecharfill(char_c);
inlineintprecision(int_i);
inlineintprecision()const;
……
//inlineoperatorvoid*()const;
operatorvoid*()const{if(state&(badbit|failbit))return0;return(void*)this;}
inlineintoperator!
()const;
……
protected:
ios();
ios(constios&);//treatasprivate
……
intstate;
longx_flags;
intx_precision;
charx_fill;
intx_width;
};
以后讨论到有关classios的问题时,可参照以上内容。
再看ostream.h:
/***
ostream.h-definitions/declarationsfortheostreamclass
***/
classostream:
virtualpublicios{
public:
ostream(streambuf*);
virtual~ostream();
ostream&flush();
…………
inlineostream&operator<<(ostream&(__cdecl*_f)(ostream&));
inlineostream&operator<<(ios&(__cdecl*_f)(ios&));
ostream&operator<<(constchar*);
inlineostream&operator<<(constunsignedchar*);
inlineostream&operator<<(constsignedchar*);
inlineostream&operator<<(char);
ostream&operator<<(unsignedchar);
inlineostream&operator<<(signedchar);
ostream&operator<<(short);
ostream&operator<<(unsignedshort);
ostream&operator<<(int);
ostream&operator<<(unsignedint);
ostream&operator<<(long);
ostream&operator<<(unsignedlong);
inlineostream&operator<<(float);
ostream&operator<<(double);
ostream&operator<<(longdouble);
ostream&operator<<(constvoid*);
ostream&operator<<(streambuf*);
inlineostream&put(char);
ostream&put(unsignedchar);
inlineostream&put(signedchar);
ostream&write(constchar*,int);
inlineostream&write(constunsignedchar*,int);
inlineostream&write(constsignedchar*,int);
ostream&seekp(streampos);
ostream&seekp(streamoff,ios:
:
seek_dir);
streampostellp();
protected:
ostream();
ostream(constostream&);//treatasprivate
ostream&operator=(streambuf*);//treatasprivate
ostream&operator=(constostream&_os){returnoperator=(_os.rdbuf());}
intdo_opfx(int);//notused
voiddo_osfx();//notused
private:
ostream(ios&);
ostream&writepad(constchar*,constchar*);
intx_floatused;
};
附注:
在C++的老版本中,_Cdecl是预定内部宏,供编译系统使用。
当它有定义(例如等于1)时,标示只选择C++语言而不选择Pascal语言。
而另一个宏_Pascal有定义时,则标示同时选择Pascal语言。
classostream_withassign:
publicostream{
public:
ostream_withassign();
ostream_withassign(streambuf*_is);
~ostream_withassign();
ostream&operator=(constostream&_os){returnostream:
:
operator=(_os.rdbuf());}
ostream&operator=(streambuf*_sb){returnostream:
:
operator=(_sb);}
};
externostream_withassigncout;
externostream_withassigncerr;
externostream_withassignclog;
从以上文件看出,classostream中所定义的各个流输出运算符“<<”是相对于各个预定义数据类型进行重载的。
也即,他们可用于各种预定义数据类型。
试看以下我们很熟悉的例子:
[例1]用于三种预定义数据类型的输出运算符
//out_1.cpp
//copiedfromp.341ofWang'sbook
#include
voidmain()
{
inti=10,j=45;
doublex=12.34,y=56.78;
char*str="Windows";
cout<<"i="<
cout<<"x="<cout<<"str="<}
/*Results:
i=10,j=45
x=12.34,y=56.78
str=Windows
*/
在以上程序中,流输出运算符采用左结合方式,允许将多个输出运算操作组合到一个语句中。
不同类型的数据也可组合到一条语句中。
8.1.2.2缓冲与非缓冲输出流
从图8.2和ostream.h中可看出,从classostream中派生出classostream-withassign,而ostream-withassign建立了三个对象:
cout、cerr和clog。
这就是标准库中定义的三个输出流。
其中cout是标准的输出流,而cerr和clog则与标准错误流相关。
其中,cout和cerr是非缓冲输出流,发送给它们的任何数据都立即输出;而clog是缓冲输出(bufferedoutput)流。
在缓冲输出流中,只当满足以下四个条件中之一时,缓存才把所存数据向文件(即标准输出设备,缺省为显示终端屏幕)输出:
(1)第一个条件:
调用flush函数:
前面看到,flush函数是classostream的成员函数。
此处它用于将输出流刷新,从而将流缓冲区的内容输出到与流相关的输出设备中。
[例1]//i_o_1_1.cpp//Touseflushfunctiontodisplayoutput#includevoidmain(){inti=12345;clog<<"ABCDE";cout<clog<}
/*Results:
12345
ABCDE*/
[例2]//i_o_1_2.cpp#includevoidmain(){inti=12345;clog<<"ABCDE"<ABCDE12345*/
注意:
cout调用flush无意义,因它是非缓冲输出流
(2)第二个条件:
程序结束:
[例3]//i_o_2.cpp
//Dataaredisplayedwhentheprogramterminates
#include
voidmain()
{
inti=12345;
clog<<"ABCDE";
cout<
}
/*Results:
12345
ABCDE*/
其实,程序结束时clog自动调用了flush函数,所以这个程序与i_o_1_1.cpp完全相同!
(3)第三个条件:
缓存满(一般为512个字节)后自动将数据输出至文件(如显示屏),并清除缓存,准备接收其它数据:
[例4]//i_o_4.cpp//textisdisplayedwhenthebufferisfull#includevoidmain(){inti=12345;
for(intc=0;c<33;c++)
clog<<"ABCDEFGHIJKLMNOP";
//totally16characterscout<sixrows(80characterseach)plus32
characters,totally512charactersdisplayed12345ABCDEFGHIJKLMNOP
(duetoprogramtermianation)*/
(4)第四个条件:
clog自己调用endl符,但不是”\n”:
[例5]//i_o_3_1.cpp
//Todisplaybyusingendl
#include
voidmain()
{
inti=12345;
clog<<"ABCDE"<cout<
}
/*Results:
ABCDE
12345*/
[例6]//i_o_3_2.cpp//clogbutnotcoutdisplaystextby
//using“endl”#include
voidmain()
{
inti=12345;
clog<<"ABCDE";
cout<clog<}
/*Results:
[CR]
12345ABCDE*/
当程序中同时出现clog和cout语句而clog和cout的语句中都是既无“flush”又无“endl”时,规则较为复杂,请参阅附录二十一。
8.1.2.3缓冲输入流
流输入运算符“>>”是在头文件istream.h的classistream中定义的。
classistream也是从classios中派生出来的。
因此下面看一下用于定义classistream的头文件istream.h。
又看istream.h:
/***
istream.h-definitions/declarationsfortheistreamclass
***/
classistream:
virtualpublicios{
public:
istream(streambuf*);
virtual~istream();
inlineistream&operator>>(istream&(__cdecl*_f)(istream&));
inlineistream&operator>>(ios&(__cdecl*_f)(ios&));
istream&operator>>(char*);
inlineistream&operator>>(unsignedchar*);
inlineistream&operator>>(signedchar*);
istream&operator>>(char&);
inlineistream&operator>>(unsignedchar&);
inlineistream&operator>>(signedchar&);
istream&operator>>(short&);
istream&operator>>(unsignedshort&);
istream&operator>>(int&);
istream&operator>>(unsignedint&);
istream&operator>>(long&);
istream&operator>>(unsignedlong&);
istream&operator>>(float&);
istream&operator>>(double&);
istream&operator>>(longdouble&);
istream&operator>>(streambuf*);
intget();
inlineistream&get(char*,int,char='\n');
inlineistream&get(unsignedchar*,int,char='\n');
inlineistream&get(signedchar*,int,char='\n');
istream&get(char&);
inlineistream&get(unsignedchar&);
inlineistream&get(signedchar&);
istream&get(streambuf&,char='\n');
inlineistream&getline(char*,int,char='\n');
inlineistream&getline(unsignedchar*,int,char='\n');
inlineistream&getline(signedchar*,int,char='\n');
inlineistream&ignore(int=1,int=EOF);
istream&read(char*,int);
inlineistream&read(unsignedchar*,int);
inlineistream&read(signedchar*,int);
intgcount()const{returnx_gcount;}
intpeek();
istream&putback(char);
intsync();
istream&seekg(streampos);
istream&seekg(streamoff,ios:
:
seek_dir);
streampostellg();
voideatwhite();
protected:
istream();
istream(constistream&);//treatasprivate
istream&operator=(streambuf*_isb);//treatasprivate
istream&operator=(constistream&_is){returnoperator=(_is.rdbuf());}
istream&get(char*,int,int);
intdo_ipfx(int);
private:
istream(ios&);
intgetint(char*);
intgetdouble(char*,int);
int_fGline;
intx_gcount;
};
classistream_withassign:
publicistream{
publ