第八章输入输出流(inputoutputstream)的基本概念及原理_第1页
第八章输入输出流(inputoutputstream)的基本概念及原理_第2页
第八章输入输出流(inputoutputstream)的基本概念及原理_第3页
第八章输入输出流(inputoutputstream)的基本概念及原理_第4页
第八章输入输出流(inputoutputstream)的基本概念及原理_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第八章输入输出流(input/output

stream)的基本概念及原理

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<stdio.h>

voidmain()

(

inti=3;

doubled=4.4;

printf(4<%d\t%f\n?,,i,d);

)

运行结果:

34.400000对!

但如写错为:

printf("%d\t%d\n”,i,d);

则编译时不出错,但运行结果错,为:

3-26214

但在C++中只须写cout«i«"”《d«endl;而不必写出变量类型!就能得出

34.4,对!

始终不会出错!

(三)通过缓存增加功能。

(四)附带优点是书写方便以及显示中没有冗余字符,能自动略去浮点数尾数中的零

(但如用户希望显示多余的零,也可以做到)。

C++的输入/输出系统是对流的操作,也即操作数据使其流向对象,或从对象流

出。

什么是流?流是从源头到目的的数据流动。当键入字符时,字符从键盘流入程序

中;当将数据写入磁盘文件中时,数据自程序流动至磁盘上。

C++输入/输出流库是使用继承方法建立起来的一个输入/输出类库,它具有两

个平行的基类,即streambuf类和iOS类。所有其它流类都是从它们直接或间接地派

生出来的。

-streambuf类用于提供物理设备的接口,它提供缓冲或处理流的通用方法。它作

为一个虚基类,具有类层次如下:

streambuf

filebufstrstreambufconbuf

图8.1

iOS类及其派生类用于为用户提供使用流类的接口。它使用streambuf完成检查错

误的格式化输入/输出操作,并支持对streambuf的缓冲区进行输入/输出时的格式

化或非格式化转换。

ios类作为流库中的一个虚基类,派生出许多派生类,其主要层次如下:

istreamfstreambaseostream

fstream

istream-withassignostream-withassign

iostream

iostream-withassign

图8.2

8.1.2预定义流(标准流)的基本原理

预定义输出输入流涉及较多的头文件有四个:ios.h,istream.h,ostream.h和

iostream.ho下面分别介绍。

8・1・2.1输出流的基本概念

流输出运算符“vv”是在头文件ostream.h的classostream中定义的。从图8.2可

以看出,classostream是从classios中派生出来的。因此下面先看一下用于定义class

ios的头文件ios.ho

先看ios.h:

*ios.h-deHnitions/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=l,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_1);

inlinelongsetf(long_fJong_m);

inlinelongsetf(long_I);

inlinelongunsetf(long_I);

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;

char

intx_width;

};

以后讨论到有关classios的问题时,可参照以上内容。

再看ostream.h:

/***

ostream.h-definitions/declarationsfortheostreamclass

classostream:virtualpublicios{

public:

ostream(streambuf*);

virtual〜ostream。;

ostream&flush(........);

ostream&endl(........);

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«(doubie);

ostream&operator«(longdouble);

ostream&operator«(constvoid*);

ostream&operator«(streambuf*);

inlineostream&put(char);

ostream&put(unsignedchar);

inlineostream&put(signedchar);

ostream&write(constchar

inlineostream&write(constunsignedchar

inlineostream&write(constsignedchar

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++的老版本中,_Cded是预定内部宏,供编译系统使用。当它有定义(例如

等于1)时,标示只选择C++语言而不选择Pascal语言。而另一个宏.Pascal有定义

时,则标示同时选择Pascal语言。

classostream_vvithassign:publicostream{

public:

ostream_withassign();

ostream_withassign(streambuf*Js);

-ostream_withassign();

ostream&operator=(constostream&_os){return

ostream::operator=(_os.rdbuf());}

ostream&operator=(streambu产_sb){returnostream::operator=(_sb);}

);

externostream_withassigncout;

externostream_withassigncerr;

externostream_withassignclog;

从以上文件看出,classostream中所定义的各个流输出运算符是相对于各

个预定义数据类型进行重载的。也即,他们可用于各种预定义数据类型。试看以下我

们很熟悉的例子:

[例1]用于三种最常用预定义数据类型的输出运算符

//out_l.cpp

//copiedfromp.341ofWang*sbook

#include<iostream.h>

voidmain()

(

inti=10,j=45;

doublex=12,34,y=56.78;

char*str=nWmdowsn;

cout«ni=M«i«Mj=M«j«endI;

cout«nx=n«x«n,y=n«y«endl;

cout«Hstr=H«str«endl;

/*Results:

i=10J=45

x=12.34,y=56.78

str=Windows

在以上程序中,输出流对象cout顺序地调用流输出运算符“<<",将一条语句中的

多个不同类型的数据依次输出。

输出流对象cout还可用于输出各类函数的运行结果,如下例:

[例2]输出各类函数的运行结果

//out_2.cpp

#include<iostream.h>

intfunl()

intj=lll;

returnj;

)

doublefun2()

(

doublej=13.57;

returnj;

)

voidmain()

(

cout«HThefirstfunhasH«funl()«M;n

«nthesecondfunhasn«fun2()«endl;

)

/*Results:

Thefirstfunhas111;thesecondfunhas13.57

从图8.2和ostream.h中可看出,从classostream中派生出class

ostream-withassign,而ostream-withassign建立了三个对象:cout、clog和ceir。这

就是标准库中定义的三个输出流对象。其中cout是标准的输出流对象,而cerr和clog

则与标准错误流有关。其中,cout和clog是缓冲输出流(burredoutputstream)

对象,发送给它们的数据暂存在缓存内,只当满足§8.123中所述条件之一时才将数

据输出至标准设备;而cerr是非缓冲输出流对象,发送给它的任何数据在执行完一条

语句后立即输出。

现在简要地看一下输出流对象cout、clog和cerr级联地运行的过程。从以上图8.2

和ostream.h中可看出:cout、clog和cerr都是classostream-withassign的对象,而

classostream-withassign是从classostream中派生出来的,因此classostream是这些

输出流对象的基类。以coutvv为例,这是对象cout调用函数operatorvv(),也即

coutoperator«()o按照支配规则,也即对象cout调用了基类的成员函数operator

«()o函数operatorvv()原型的返回值是classostream对象的引用,而实际上则返

回classostream的派生类classostream-withassign的对象cout的引用。此返回的对

象可继续调用下一个函数operator«()o直至整条语句末尾。

如只从表面看,有些现象看不清楚,见以下两例:

[例1]变量值出错

//out_10.cpp

//trapofcout,clogandcerrstatements

#include<iostream.h>

voidmain()

inti=10;

cout«nfirsti=M«i«n,secondi=n«i++«endl;

/*Results:

firsti=ll,secondi=10

*/

以上程序中,原来指望所显示的两个i都等于10,结果不然。为何?

[例2]第七章§7.1"程序运行错误及其处理”中[例1]程序sqrt_negative.Lcppo

该程序中主函数第二语句^cout«HSqrtof-4isn«sqroot(-4)«endl;w出错,但并不

显示字符串“Sqrtof-4is”。

[例3]有些输出语句并不立即输出数据。

//out_4.cpp

//Touseendl()functiontodisplayoutputcontents

#include<iostream.h>

#include<conio.h>//forgetch()

voidmain()

(

char*cl2=H12345H;

cout«cl2«HABCDH«cl2;

getch();

cout«MABCDEFGH«endl;

/"Resultsz

只在击键二次之后,才能显示以下内容:

12345ABCD12345ABCDEFG

*/

以上程序中,主函数的第二语句运行完后,并无输出,也即屏幕上并无显示。何

故?这些都要求、也只能从汇编语言的更深层次来解释。

整句cout、dog或cerr输出语句的运行都可分为三个阶段:入栈阶段、缓存阶段

和输出阶段。详述如下。

第一阶段…入栈阶段

此阶段由主函数main()完成。主函数按照逆顺序将整条输出语句中需要输出的各

项数据或数据的存放地址压入栈区。其中:

(a)如果数据是单个数据(无论预定义类型或是用户自定义类型),则直接将该项数

据压入栈区。其中整型数据或单个字符占4个字节(堆栈按照4字节边界原则运

行,4-byteboundary),double型数据占8个字节,类的对象则可能占更多空间,

(b)如果数据是数组,则系统将该数组放置于数据区内,并将此数组首址压入栈区。

(C)如果数据是一个函数运行后的返回值,则系统执行该函数,并根据该函数返回

只的数值类型,按照上两项规定来决定将该数据或是数据地址压入栈区。

第二阶段一缓存阶段

此阶段由重载运算符函数<<完成。它取出栈区内所存数据或按数据地址找到数据

本身,并将该项完整的数据存入位于堆区内的缓存。如缓存是空的,则该数据被放置

于缓存的首址(例如§8.122"非缓冲输出流”);如缓存内已存有其它数据,则该

数据被放置于缓存内已有数据之后(例如§8.123“缓冲输出流”)0

第三阶段…输出阶段

此阶段由另一些重载运算符函数<<调用flush函数来完成,它们最终调用硬件接

口的函数_imp_WriteFile,直接将数据输出至标准设备(显示屏幕)。由于函数

_imp_WriteFile无法访问堆区,因此必须再将数据从堆区内再转存至栈区内,才能

供函数_imp_WriteFile调用后输出。flush函数顺序地从缓存(堆区)内取出所存

全部数据,从新压入至栈区另外的地址,以备输出。接着函数_imp_WriteFile直接

将位于栈区新地址处的全部数据输出至标准设备(显示屏幕)。

下面将分别介绍缓冲输出流对象和非缓冲输出流对象,它们在执行输出语句时的

差别在于:

(1)每当非缓冲输出流对象cerr语句运行时,在入栈阶段之后,它调用重载运

算符函数接连执行缓存阶段和输出阶段,直接将全部数据输出至标准设备(显

示屏幕)。

(2)每当缓冲输出流对象cout或clog调用重载运算符函数<<时,一般情况下只

执行入栈阶段和缓存阶段,只将数据存入缓存而不输出数据。只当满足§8.123中所

述条件之一时才执行输出阶段的操作,通过运算符函数从堆区中的缓存内取出

所存全部数据,调用用于控制硬件接口的函数_imp_WriteFile,直接将全部数据输

出至标准设备(显示屏幕)。

非缓冲输出流的运行机理

cerr是非缓冲输出流对象,发送给它的所有数据在整条输出语句(不是半条语句)

运行后立即输出至标准设备。

[例1]非缓冲输出流对象cerr一次将整条语句的数据顺序地输出至标准设备。

//out_3.cpp

//''cerr''outputsthecontentsofonestatementimmediately

#include<iostream.h>

intfunl()

{

intj=512;

returnj;

)

doublefun2()

(

doublej=12.34;

returnj;

)

voidmain()

(

cerr«HThefirstresultisn«funl()«*;*

«nthesecondresultisn«fun2();

/*Results:

Thefirstresultis512;thesecondresultis12.34

*/

以上程序。ut_3.cpp中只有一句cen•输出语句,整个语句的运行分为两大步:第

一大步就是入栈阶段;第二大步包括缓存阶段和输出阶段。

第一大步…入栈阶段

主函数按照逆顺序将整条语句中需要输出的各项数据或数据的存放地址压入栈

区。其顺序操作如下:

(a)系统执行函数fun2(),其运行结果为12.34。此数据被压入栈区,占8个字节。

(b)第二项被压入栈区的数据是字符串“thesecondresultis”在数据区内的地址

(0042a038)o

(c)第三项被压入栈区的数据是字符「(其ASCH码值为3Bh),占4个字节。

(d)系统接着执行函数funl(),其运行结果512被压入栈区,占4个字节。

(e)第五项也即最后一项被压入栈区的数据是字符串“thefirstresultis”在数据区

内的地址(0042a01c)。

Table_out_l:程序out_3.cpp入栈阶段后的栈区内容

低地址0x0042a01c

0x0042a038

高地址

请注意:以上数据压入栈区的顺序是从输出语句的末端开始的。

第二大步:缓存阶段和输出阶段

非缓冲输出流对象cerr是逐个地输出数据的。对于每个数据,非缓冲输出流对

象cen•都调用运算符函数并一气呵成地完成数据缓存和输出的操作,直接将该

项数据输出至标准设备(显示屏幕)。

其详细过程请参阅附录二十六“非缓冲输出流对象cerr输出数据的详细过程”。

由以上可见,在入栈阶段中要求所有函数funl()和fun2()都正常运行,并返回运

行结果,然后才能进入第二和第三阶段。如果程序运行发生意外而导致程序中断,则

整条输出语句的数据将没有一项能够输出或显示。由于这个原因,第七章的多个程序

中,当主函数的语句运行过程中抛出异常因而导致程序意外中断时,整条语句的所有

数据都不能显示。请见下节中[例5]和[例6]。

缓冲输出流的运行机理

在缓冲输出流中,只当满足以下四个条件中之一时,系统才把所存数据从缓存向

标准输出设备(缺省为显示终端屏幕)输出:

(1)第一个条件:调用endl或flush函数:

前面看到,flush和endl函数都是classostream的成员函数。此处flush函数用于

将输出流刷新,从而将缓存的内容输出到与流相关的输出设备中。而endl函数则调

用了flush函数,除将输出流刷新外,还执行回车操作。

[例1]调用endl函数以便刷新缓存

//out_4.cpp

//Touseendl()functiontodisplayoutputcontents

#include<iostream.h>

#include<conio.h>//forgetch()

voidmain()

(

char*cl2=H12345H;

cout«cl2«nABCDH«cl2;

getch();

cout«HABCDEFGH«endl;

Results:

只在击键二次之后,才能显示以下内容:

12345ABCD12345ABCDEFG

(这说明在调用endl函数之前,cout语句不输出任何数据)

*/

在out_4.cpp的例子中,共有两条cout语句。其处理流程为:第一条cout语句不

调用endl函数,只执行前两个阶段,即入栈阶段和缓存阶段;第二条cout语句调用

endl函数,除执行前两个阶段外,还执行输出阶段。

(-)入栈阶段和缓存阶段-在out_4.cpp的例子中:

(a)先处理第一条cout语句。主函数按照逆顺序将整条语句中需要输出的三项数据

的存放地址压入栈区,如下:

Table_out_2:out_4.cpp第一条cout语句入栈阶段后的栈区内容

低地址0x0042a024(字符串“12345”地址

0x0042a038(字符串“ABCD”地虾"

高地址0x0042a024(字符串“12345”地址厂

然后由输出流对象cout调用重载运算符函数顺序地分别按照栈区内的地址取出

数据区内字符串“12345"、字符串“ABCD”和字符串“12345”,并按顺序存入缓存内

(位于堆区内,其首址为p=0x00441810)。

(d)接着处理第二条cout语句。除执行入栈阶段和缓存阶段外,还执行输出阶段。

首先执行入栈阶段和缓存阶段,主函数继续按照逆顺序将整条语句中需要输出的各项

数据或数据的存放地址压入栈区。

Table_out_3:out_4.cpp第二条cout语句入栈阶段后的栈区内容

低地址0x0042a01c(字符串"ABCDEFG”地址)

0x0040101e(函数endl的地址)

高地址

接着输出流对象cout调用重载运算符函数按照栈区内的地址取出数据区内字

符串"ABCDEFG”,存入缓存内第三项数据之后。

(二)输出阶段:由于栈区内最后一个所存数据是函数endl的地址(00401010,重

载运算符函数<<按此地址调用该函数。从而执行第三阶段即输出阶段。系统顺序地

从缓存内(位于堆区内,其首址为p=0x00441810)取出所存全部数据,并将其全部

压入栈区一定地址(在out_4.cpp程序中其首址在lfbuf=0012f97c处),以供函数

_imp_WriteFile调用输出。

其详细过程请参阅附录二十七“缓冲输出流对象调用endl函数的详细过程”。

上例的第二条cout语句中,如用flush函数替代endl函数,或者用clog替代cout,

程序的运行结果将完全相同。

(2)第二个条件:程序结束:

[例2]在程序结束之前,clog不输出任何数据

//out_5.cpp

//Todisplayoutputcontentsattheendoftheprogram

#include<iostream.h>

#include<conio.h>//forgetch()

voidmain()

(

char*cl2=H12345

c!og«nABCDn«cl2«nABCDEFG

clog«cl2;

getchQ;

/*Resultsz

只在击键二次之后,才能显示以下内容:

ABCD12345ABCDEFG12345

(这说明在程序结束之前,clog语句不输出任何数据)

*/

在以上程序。ut_5.cpp的数据输出阶段中,函数main()结束时,程序返回系统函

数mainCRTStartup()(系统是由此进入函数main()的),并在此函数中调用函数exit。

然后几经周折,调用函数—write。系统取出堆区内所存全部数据(out_5.cpp例中首

址在p=0x00441810),并将其全部压入栈区一定地址(out_5.cpp例中其首址在

lfbuf=0x0012fadc处),以供函数_imp_WriteFile输出,直接将所有数据输出至标

准设备(显示屏幕)。

(3)第三个条件:当出现cerr语句或dn语句时:

[例3]在执行非缓冲输出流对象cerr语句前,cout不输出任何数据

//out_6.cpp

//Nodisplayof"cout"until"cerr"statement

#include<iostream.h>

#include<conio.h>

voidmain()

char*cl2="12345

cerr«"ABCDEFG

cout«cl2«"ABCD

getch();

cerr«cl2;//No"endl"

/*Results:

ABCDEFG(击键一次后继续显示)12345ABCD12345(但不回车)

*/

整个。ut_6.cpp程序包括三条输出语句:两条cerr语句(非缓冲输出流对象)和

一条cout语句(缓冲输出流对象)。

程序第一条输出语句是cerr语句。整句cerr输出语句的运行如同§8.122“非缓

冲输出流"[例1]中程序out_3.cpp一样。

程序第二条输出语句是cout语句。整句cout输出语句的运行只是执行入栈阶段

和缓存阶段,其结果分别将字符串”12345“和“ABCD”存入缓存(首址在

0x00431570)内。

程序第三条输出语句是cerr语句。整句cerr输出语句的运行分为三个阶段:

在入栈阶段中,主函数先将数据(字符串”12345")的地址压入栈区;在进入缓

存阶段之前,非缓冲输出流对象cerr通过重载运算符函数<<调用flush函数,将缓存

清空,因而将缓存内全部数据(字符串“12345ABCD")转存至栈区,通过函数

_imp_WriteFile将数据输出至标准设备(显示屏幕),其结果是显示字符串”12345

ABCD”。接着进入正常的缓存和输出阶段,其操作和本程序中第一条cen•语句相同:

显示字符串”12345"0

Tableout4:out6.cpp第2句即cerr语句入栈阶段后的栈区内容

低地址0x00426024(字符串"ABCDEFG”地址)

高地址--------------------------------------

Table...out.5out6.cpp第3句即cout语句入栈阶段后的栈区内容

低地址0x00426030(字符串”12345”地址)

-0x0042601c(字符串“ABCD”地址)

Tableout6:out6.cpp第5句即ceir语句入栈阶段后的栈区内容

低地址0x00426030(字符串”12345”地址)

高地址

其详细过程请参阅附录二十八”在执行非缓冲输出流对象cerr语句前,缓冲输出

流对象cout不输出任何数据”。

(4)第四个条件:缓存满(一般为512个字节)后自动将数据输出至标准设备,并

清除缓存,准备接收其它数据:

[例4]512个字节的缓存满后自动将数据输出至显示屏,并清除缓存

//out_7.cpp

//textsaredisplayedwhenthedataoverflowthebuffer

#include<iostream.h>

#include<conio.h>//forgetch()

voidmain()

(

for(intc=0;c<8;c++)

cout«HABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLM

NOPQRSTUVWXYZZYXWVUH;

//totally64*8=512characters

getch();

cout«endl;

getch();

cout«n12345H;

/*Results:

(第一条cout循环语句共有512个字符,正好是一个缓存的长度,但缓存虽满,

尚未溢出,故不刷新,也不输出数据。必须继续存入数据(此处为回车符),

使缓存溢出方能刷新,因此必须击键一次后方能显示以下512个字符)

ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRS

TUVWXYZZYXWVUABCDEFGHIJKLMNOP

QRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUAB

CDEFGHIJKLMNOPQRSTUVWXYZZYXWVU

ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRS

TUVWXYZZYXWVUABCDEFGHIJKLMNOP

QRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUAB

CDEFGHIJKLMNOPQRSTUVWXYZZYXWVU

ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRS

TUVWXYZZYXWVUABCDEFGHIJKLMNOP

QRSTUVWXYZZYXWVUABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUAB

CDEFGHIJKLMNOPQRSTUVWXYZZYXWVU

ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVU(又击键一次后再次显示:)

12345

*/

在以上程序out_7.cpp中,当缓存刚好充满时,运算符函数并不将缓存(首址为

0x00441880)刷新。只当继续存入数据(此处为回车符),使缓存溢出时,运算符函

数<<才将缓存(首址为0x00441880)刷新。也即将堆区内地址0x00441880处所存全

部数据(共512个字节)复制至栈区(lfbuf=0x0012f920)处,然后即输出至标准设

备(显示屏幕),显示512个字符。最后一条cout语句只是将字符串“12345”压入栈

区,并接着将它们转存入堆区(首址在0x00441880)。当此cout语句调用endl函数

时,即调用flush函数,刷新该堆区,将字符串”12345”存入栈区(首址在0x0012fadc),

供函数_imp_WriteFile调用,将数据输出至标准设备(显示屏幕),加以显示。

§8.122"非缓冲输出流”末尾处提到:如果程序运行发生意外而导致中断,则整

条输出语句的数据将没有一项能够输出或显示。请见以下两例。

[例5]程序未发生意外,cout正常显示

//out_8.cpp

//ncoutndisplaysresultofoperationoffun()normally

#include<iostream.h>

intfun()

(

intj=45678;

returnj;

)

voidmain()

(

cout«Hfun()hasaresultofH«fun()«endl;

/*Results:

fun()hasaresultof45678

*/

[例6]程序发生意外,cout无任何显示

//out_9.cpp

//''cout''doesnotdisplayanythingwhenfun()isterminatedabnormally

#include<iostream.h>

#include<stdlib.h>//forexit(l)

intfun()

(

intj=1234;

if(j==1234)exit(l);

returnj;

)

voidmain()

(

cout«Hfun()hasaresultofn«fun()«endl;

/*Results:

Nothingisdisplayed

缓冲输入流的运行机理

流输入运算符”是在头文件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&);

inlineistreain&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(charint,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='\iT);

inlineistream&getline(signedchar*,int,char='\n');

inlineistream&ignore(int=l,int=EOF);

istream&read(char

inlineistream&read(unsignedchar

inlineistream&read(signedchar

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&Js){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{

public:

istream_withassign();

istream_withassign(streambuf*);

-istream_withassign();

istream&operator=(constistream&_is){returnistream::operator=(_is);}

istream&operator=(streambuf*_isb){returnistream::operator=(_isb);}

);

externistream_withassigncin;

最后J暝便看一下iostream.h:

iostream.h-definitions/declarationsforiostreamclasses

***/

typedeflongstreamoff,streampos;

#include<ios.h>//Defineios.

#include<istream.h>//Defineistream.

#include<ostream.h>//Defineostream.

class_CRTIMPiostream:publicistream,publicostream{

public:

iostream(streambuf*);

virtual-iostream();

protected:

iostream();

iostream(constiostream&);

inlineiostream&operator=(streambuf*);

inlineiostream&operator=(iostream&);

private:

iostream(ios&);

iostream(istream&);

iostream(ostream&);

};

从图8.2和istream.h中可看出,从classistream中派生出classistream-withassign,

而istream-withassign建立的一"对象就是"cin"。

cin是缓冲输入流对象。

运算符“>>”在读取数值时,被输入的数值之间的缺省隔离符是空格符一或换行

符。当输入非有效字符(例如当输入整数值时,输入的小数点即为非有效字符)时,

此非有效字符也被认为是一个隔离符。

此时,上一个数值的输入操作结束。如果此非有效字符正好是下一个输入数值的

有效字符(例如下一次正好读取浮点数)则此非有效字符被存入缓存内,作为下一次

输入操作的有效字符(见以下[例1]至[例4])0

但如果这个非有效字符并不是下一个输入数值的有效字符(例如下一次仍然读取

整数),则输入操作失败,将0输入下一个变量,同时结束整个输入操作(见以下[例

5])o

真正将数值输入至变量中的操作必须在键入换行符后才开始。

空格符和回车符的使用:

空格符是数据隔离符(delimiter),只用于将数据输入至缓存内;而回车符才用于

将数据从缓存中输入至程序的数据变量中。

[例1]前一个数值的非有效字符是后一个数值的有效字符

//in_l.cpp

//Thenoneffectivecharacteroftheformervalue

//isaneffectivecharacterofthelattervalue

#include<iostream.h>

voidmain()

(

inti,j;

doubled;

cout«"Pleaseinputthreevalues:";

cin»i»j»d;

cout«i«""«j«""«d«endl;

/*Results:

Pleaseinputthreevalues:1234.5678

12340.56

*/

[例2]小数点在输入浮点数时的作用

//in_2.cpp

//Theroleofadigitpointwhenreading

//infloatingvalues

#include<iostream.h>

voidmain()

(

doubled,e,f;

cout«nPleaseinput:";

cin»d»e»f;

cout«d«Mn«e«f«endl;

)

/*Results:

Pleaseinput:.5.6.7

1.20.30.4

[例3」]两个连续的词的输入

〃in_6・cpp

IIfig04_12.cppofDeitels*bookwithmistakecorrected

//Treatingcharacterarraysasstrings

#include<iostream.h>

voidmain()

(

charstring[20];

cout«MEnterastring:n;

cin»string;

cout«”stringis:n«string«endl;

cout«HEnterastring:n;

cin»string;//readsagain

cout«u\nstringis:''«string«endl;

)

/*Results:

Enterastring:Hellogoodfriend

stringis:Hello

Enterastring:

stringis:good

[例3_2]输入多串字符串

//in_3・cpp

//Readincharacters

#include<iostream.h>

voidmain()

charc[10],ch[10],cha[10];

cout«HPleaseinput:";

//spacekeyisadelimiter(隔离符)

cin»c»ch»cha;

cout«c«MM«ch«MH«cha«endl;

)

/*Results:

Pleaseinput:IamaChinese

Iama*/

[例3_3]输入更多词

//in_4.cpp

//Readincharacters

#include<iostream.h>

voidmain()

(

charc[10],ch[10],cha[10];

cout«nPleaseinput:'';//spacekeyisthedelimiter

cin»c»ch»cha;

cout«c«''''vvch«Mn«cha«endl;

cin»c»ch;

cout«c«''''«ch«endl;

)

/*Results:

Pleaseinput:!amaChinesestudent

Iama

Chinesestudent*/

[例4]空格和回车的不同功能

//in_5.cpp

//DifferentfunctionsofspaceandCR

#include<iostream.h>

voidmain()

(

inti9a[3];

cout«nPleaseinputintegers:

for(i=0;i<3;i++)cin»a[i];

for(i=0;i<3;i++)cout«a[i]«endl;

cout«HPleaseinputagain:

for(i=0;i<3;i++)cin»a[i];

for(i=0;i<3;i++)cout«a[i]«endl;

/*Twokindsofresults:

(l)Pleaseinputintegers:1234(CR)(CR即carriagereturn)

1

2

3

Pleaseinputagain:5678(CR)

4

5

6

(2)Pleaseinputintegers:1(CR)

2(CR)

3(CR)

1

2

3

Pleaseinputagain:纵CR)

5(CR)

6(CR)

4

5

6*/

与以上程序不同,当上一个非有效字符不是下一个输入数值的有效字符时,输

入操作结束。

[例5]应输入整数值时,如误输入小数点或字符类型,则将0输入至下一个变量,同

时结束整个输入操作

//in_7.cpp

//Toshowthatwronginputtedvaluetypewillterminateinputoperation

#include<iostream.h>

voidmain()

(

inta=l,b=2,c=3,d=4;

cout«HOriginalvalues:n«a«*/«b«,/«c«,/«d«endl;

cout«Hinputfourintegers:n;

cin»a»b»c»d;

if(!cin)cout«nWrongtypeofinputteddatum!Valuesbecome:

elsecout«nInputtedvalues:n;

cout«a«,/«b«,/«c«,/«d«endl;

)

/*Twokindsofresults:

(l)correctoperation:

Originalvalues:1,2,3,4

inputfourintegers:11223344

Inputtedvalues:11,22,33,44

(2)incorrectoperation:

Originalvalues:1,2,3,4

inputfourintegers:12.345

Wrongtypeofinputteddatum!Valuesbecome:1,2,0,4

或者

Originalvalues:1,2,3,4

inputfourintegers:1p23

Wrongtypeofinputteddatum!Valuesbecome:1,0,3,4

*/

以上程序中,使用运算符函数“ios::operator!”检验输入流对象“cin”的输入操

作是否成功。cin的内存存储空间中有一个单元state,用于存储与classios的公有数

据成员io_state相对应的各位的数值。如输入操作成功,则此state单元中设置goodbit

=0x00;如输入操作失败,则此state单元中设置failbit=0x02,显示输入操作出错。

这使用classios的成员函数operator!(),如下:

inlineintios::operator!()const{returnstate&(badbit|failbit);}

当输入操作成功,state域内badbit和failbit都为零,因此此重载运算符函数返回

零值。否则返回非零值,表示出错。

应该指出,cin用于读取字符串时,用于存放所读字符串的缓存空间必须是在栈

区或堆区内,而不应在数据区内。因数据区内的字符串或字符数组都是常量数组,

不能更改,无法用于存放所读字符串。请见以下程序:

[例6]//str_ptr_&_array_5.cpp

//Toseedifferentchar-pointershavingdifferentbehaviors

#include<iostr

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论