收藏 分享(赏)

面向对象程序设计 ch10.ppt

上传人:gnk289057 文档编号:7960913 上传时间:2019-05-31 格式:PPT 页数:92 大小:958KB
下载 相关 举报
面向对象程序设计 ch10.ppt_第1页
第1页 / 共92页
面向对象程序设计 ch10.ppt_第2页
第2页 / 共92页
面向对象程序设计 ch10.ppt_第3页
第3页 / 共92页
面向对象程序设计 ch10.ppt_第4页
第4页 / 共92页
面向对象程序设计 ch10.ppt_第5页
第5页 / 共92页
点击查看更多>>
资源描述

1、第10章 运算符重载及流类库 在建立了自己的类以后,C+允许程序员重新定义C+中已有的运算符,通过运算符重载,就可像处理基本数据类型那样使用它们。为了面向对象编程的需要,C+提供了一个用于输入输出(I/O)操作的类体系,这个类体系提供了对预定义类型进行I/O操作的能力,程序员也可以利用这个类体系进行自定义类型的I/O 操作。本章将简要介绍运算符重载的基础知识、流类库的概念及使用流类库进行文件存取的基本方法。,主要内容,10.1 运算符重载 10.2 流类库 10.3 文件流 10.4 文件读写综合实例,10.1 运算符重载本节首先引入运算符重载的必要性,然后讨论类运算符和友元运算符的异同。,1

2、0.1.1 重载对象的赋值运算符 编译器在默认情况下为每个类生成一个默认的赋值操作,用于同类的两个对象之间相互赋值。默认的含义是逐个为成员赋值,即将一个对象的成员的值赋给另一个对象相应的成员,这种赋值方式对于有些类可能是不正确的。假设类str的数据成员是“char *st”,下面语句str s1( “hello“ ), s2( “world“ );s2 = s1; 经赋值后,s2.st和s1.st是同一块存储地址(如图7.1所示)。当s2和s1的生存期结束时,存储“hello”的变量被删除2次,这是个严重的错误。另外,对于“s1=s1”的情况,也应不执行赋值操作。因此,程序必须为str类定义自

3、己的赋值操作“=”。这个操作应该能够保证s1.st=s2.st,但两者各自具有自己的存储地址(如图7.2所示)。如果发现本身自己赋值,则不执行赋值操作。先不管如何声明这个“=”函数,使用“赋值函数”一词代表这个操作,则可像下面这样实现它:,str /返回this指针指向的对象 这个成员函数必须使用引用参数,“赋值函数”使用符号“operator=”表示,C+的关键字“operator”和运算符一起使用,表示一个运算符函数,例如“operator+”表示重载“+”运算符。读者应将operator=从整体上视为一个(运算符)函数名。运算符函数将在后续章节介绍。,这样就可将它声明为“str 即s2调

4、用成员函数str : operator = ( str&)完成赋值操作。,因为这个函数返回一个引用,所以它可以用于下面这种赋值操作中:s3 = s2 = s1; C+编译器将其解释为s3.operator = ( s2.operator = ( s1 ) ); 下面给出str类的完整实现和测试主程序,以便读者对比分析。 由此可见,它们虽然是函数,但完全可以不写成函数调用,而采用原来的书写习惯,系统会自动按其真正的含义执行。运算符重载其实就是函数重载,抓住这个实质,就很容易理解了。 【例10.1】 完整实现str类的例子。 #include #include using namespace st

5、d;,class str private:char *st;public:str( char *s );str( str,str : str( str,str /调用赋值操作符,s3=“Go home!“; /调用字符串赋值操作符s3=s3; /调用赋值操作符但不进行赋值操作s1.print(); s2.print( ); s3.print(); 程序中定义两个赋值运算符,是因为程序中经常要进行形如s1 = “hello“; 这样的赋值,因此为str类定义了一个成员函数str& str : operator = ( char *s ),以绕过类型转换所带来的运行时间开销。 程序运行结果如下:

6、We They We We,We Go home! 显然,主程序与写成如下函数调用形式的效果一样。 void main( ) /调用构造函数和复制构造函数str s1(“We“), s2(“They“),s3(s1); s1.print(); s2.print(); s3.print();s2.operator=(s1).operator =(s3); /与s2=s1=s3等效s3=“Go home!“; /调用字符串赋值操作符s3.operator = (s3); /与s1=s1等效s1.print(); s2.print( ); s3.print(); ,10.1.2 运算符重载的实质 读

7、者已经熟悉了函数重载,按此推理,表达式 7/2=3,而7.0/2.0=3.5 。这里的同一个运算符“/ ”,由于所操作的数据不同而具有不同的意义,称为“运算符重载”也就顺理成章了。这个“/”的重载就是系统预先定义的运算符重载。C+ 是由函数组成的,在C+内部,任何运算都是通过函数来实现的。在处理表达式 5+2时,因为“operator +”是“+”的函数形式,所以C+ 将这个表达式解释成如下的函数调用表达式:operator + ( 5, 2 ); 然后就去寻找,看看有没有以operator + (int,int)为原型的函数。因为系统已经定义了一个这样的函数,于是就调用这个函数。因为任何运算

8、都是通过函数来实现的,所以运算符重载其实就是函数重载,要重载某个运算符,只要重载相应的函数就可以了。与以往稍有不同的是,需要使用新的关键字“operator”,它经常和C+的一个运算符连用,构成一个运算符函数名,例如“operator +”。,这种构成方法就可以像重载普通函数那样,重载运算符函数operator+( )。 由于C+已经为各种基本数据类型定义了该运算符函数,所以只需要为自己定义的类型重载operator + ( )就可以了。 一般地,为用户定义的类型重载运算符,都要求能够访问这个类型的私有成员。所以只有两条路可走:要么将它重载为这个类型的成员函数,要么将它重载为这个类型的友元。为

9、区别这两种情况,将作为类的成员函数称为类运算符,而将作为类的友元的运算符称为友元运算符。 C+的运算符大部分都可以重载,不能重载的只有“. ”、“:”、“*”和“? :”。前面3个是因为在C+中有特定的含义,不准重载可以避免不必要的麻烦;“? :”则是因为不值得重载。另外,“sizeof”和“#”不是运算符,因而不能重载,而= 、()、-这4个运算符只能用类运算符来重载。,10.1.3 和+运算符重载实例 为了加深理解,本节再结合函数调用,分别给出使用友元运算符和类运算符的例子。 其实,插入符“”的重载也与其他运算符重载一样,只是它们必须作为类的友元重载,因为操作符的左边是流而不是被操作的对象

10、。除非违反习惯将流放在操作符的右边,但这样既会影响程序的可读性,也违反了操作符重载的原则。 插入符函数的一般形式为:ostream 显然,插入符函数的第2个参数使用引用方式比直接使用对象名的可读性要好一些。,提取符函数的一般形式为: istream ,public:test(int a=0, float b=0, char c=0)i=a; f=b; ch=c;friend ostream ,istream ,operator (cout,B);operator (cout,C); 运行示例如下: 45,8.5,W Input as i f ch:5 5.8 A 2 3.4 a /假设输入两组

11、 5,5.8,A 2,3.4,a 将主函数写成上面的函数调用形式,是为了演示运算符就是函数重载。一般在使用时,则直接使用运算符。下面是正规的使用方式: void main() test A(45,8.5,W);coutA;,test B,C;coutBC;coutBC; 显然,运算符“”重载函数有两个参数,第1个是ostream 类的一个引用,第2个是自定义类型的一个对象。这个重载方式是友元重载。另外,这个函数的返回类型是一个ostream 类型的引用,在函数中实际返回的是该函数的第1个参数,这样做是为了使得“”能够连续使用。例如,对于语句cout a b; /a,b均为自定义类型的对象 第1

12、次,系统把 cout a 作为operator ( cout,a); 来处理,返回cout,紧接着又把刚返回的cout连同后面的“ b”一起作为,operator using namespace std;class number int num;public:number( int i ) num=i; int operator + ( ); / 前缀:+nint operator + ( int ); / 后缀:n+void print( ) cout “num=“num end; ;,int number : operator + ( ) num +;return num;int numb

13、er : operator + ( int ) /不用给出形参名 int i=num;num +;return i;,void main( ) number n(10);int i = +n; / i=11, n=11cout “i=“iendl; / 输出i=11n.print(); / 输出n=11i=n+; / i=11, n=12cout “i=“ i endl; / 输出i=11n.print( ); / 输出n=12 同理,如果主函数的第2条和第5条语句使用函数调用方式,则分别为: int i=n.operator + ( );,i=n.operator +(0); 由此可见,只要

14、定义正确,不必再使用函数调用方式,而直接使用运算符。 【例10.4】 使用友元运算符重载“+“运算符。 为友元运算符需要要修改操作数,所以必须使用引用参数。程序如下: #include using namespace std; class number int num;public:number( int i ) num=i; friend int operator + ( number/ 后缀:n+,void print( ) cout “num=“ num endl; ;int operator + (number仍然使用上面的主程序,则运行结果一样。 有些C+编译器不区分前辍或后辍运算符

15、,这时只能通过,对运算符函数进行重载时来反映其为前辍或后辍运算符。 注意不能够自己定义新的运算符,而只能是把C+原有的运算符用到自己设计的类上面去。同时,经过重载,运算符并不改变原有的优先级,也不改变它所需的操作数数目。当不涉及到定义的类对象的时候,它仍然执行系统预定义的运算,只有用到自己定义的对象上,才执行新定义的操作。 应该根据需要进行运算符重载。不排除在某些特殊情况下会有一些特殊的需要,但大多数情况下不会将运算符“+”重载为两个复数相减的运算(尽管有能力这么做)。一般总是要求运算符重载合乎习惯。 尽管C+有那么多运算符可以重载,但实际中真正需要去重载的运算符却没有几个。所介绍的“+”重载

16、,结论对“-”完全适用。 举一反三,四则运算就不会有什么问题了。,10.1.4 类运算符和友元运算符的区别 如果运算符所需的操作数,尤其是第一个操作数希望进行隐式类型转换,则该运算符应该通过友元来重载。另一方面,如果一个运算符的操作需要修改类对象的状态,则应当使用类运算符,这样更符合数据封装的要求。但参数是使用引用还是对象,则要根据运算符在使用中可能出现的情况来决定。例如,对复数类使用友元函数重载“+”运算符,可以写出如下格式:friend complex operator + (形参1,形参2) /函数体定义 关键字friend把 complex operator + (形参1,形参2)说明

17、成类的友元,使得它能够访问类complex 的私有数据。形参可以都是对象或者对象的引用,也可以一个为对象一个为引用,这要视具体问题而定。 下面仅仅给出使用对象和引用的两种函数原型声明。,friend complex operator +(complex ,complex); /对象 /对象引用 friend complex operator +(complex ,double i=c2.imag+c1.imag;return complex(r, i); 【例10.5】使用对象作为友元函数参数来定义运算符“+”的例子。 因为C+定义了复数模板,所以这里不使用命名空间,包含的是iostream.

18、h文件。 #include class complex private:double real, imag;public:complex(double r=0, double i=0) real=r; imag=i;friend complex operator + (complex, complex);void show()coutreal“+“imag“i“;,complex operator + (complex a,complex b)double r = a.real + b.real;double i = a.imag + b.imag;return complex(r,i);vo

19、id main() complex x(5,3), y ;y =x+7;y =7+y;y.show(); ,程序运行正常,因为语句“y =x+7;”和语句“y =7+y;”可以分别解释为:y =operator +(x,7);y =operator +(7,y); 而“7”均可通过构造函数转换成complex类型的对象,使其参数匹配,保证正常工作。 如果将第1个参数换为引用,则语句“y =7+y;”等价为“y=7.operator+(y);”,则系统无法解释这个式子的含义。同理,如果将第2个参数换为引用,则语句“y=x+7;”等价为“y=x.operator+(7);”,系统也无法解释这个式子

20、的含义。 由此可见,如果对象作为重载运算符函数的参数,则可以使用构造函数将常量转换成该类型的对象。如果使用引用作为参数,因为这些常量不能作为对象名使用,所以编译系统就要报错。假如将上面友元运算符均使用引用作为参数,则“y =x+7;”和“y =7+y;”都不能通过编译。在使用运算符重载时,必须分清场合及其使用方法。,同理,如果将【例10.5】中的友元运算符换为类运算符,假设类运算符为如下形式:complex operator + (complex a)double r = a.real + real;double i = a.imag + imag;return complex(r,i); 因

21、为“y=x+7;”等价为“y=x.operator+(7);”,而“7”可通过构造函数转换成complex类型的对象,所以使其参数匹配,从而保证正常工作。而“y =7+y;”等价为“y=7.operator+(y);”,则系统无法解释这个式子的含义。如果参数使用引用形式,则“y=x+7;”也无法通过编译。 成员运算符比友元运算符少一个参数,这是因为成员函数具有this指针。complex &a是形参表,a是类complex的引用对象,所以使用对象名。a.real代表对象a的数据成员,real,而real则为当前对象的数据成员,它用以完成两个复数的加法运算。 C+同时具有类运算符和友元运算符,参

22、数也可以使用对象或引用,这就给编程带来了极大的方便,但也要注意各自适用的特定场合。,10.1.5 下标运算符“ ”的重载 运算符 只能用类运算符来重载。下面例子中的一位数组iArray类完全遵照C+关于数组的规定:数组an,其下标从0开始,n-1结束。 【例10.6】设计类iArray,对其重载下标运算符 ,并在进行下标访问时检查下标是否越界。 #include #include using namespace std; class iArrayint _size; int *data;public:iArray(int);int,int size()constreturn _size;iAr

23、ray()delete data; ; iArray:iArray(int n) /构造函数中n1 if(n1)cout“Error dimension description“;exit(1);_size=n;data=new int_size; int&iArray:operator (int i) /合理范围0_size-1 ,if(i_size-1) /检查越界cout“nSubscript out of range“;deletedata;exit(1);return datai; void main()iArray a(10);cout“数组元素个数=“a.size()endl;f

24、or(int i=0; ia.size(); i+)ai=10*(i+1);for(i=0; ia.size(); i+)coutsetw(5)ai; ,注意数组1n个元素对应的下标为0n-1。可用couta-1; 和couta-1; 等语句检查越界,可用“iArray b(0);”语句检查构造函数。程序运行结果如下: 数组元素个数=10 10 20 30 40 50 60 70 80 90 1009.1.6,10.2 流类库 C+的流类库由几个进行I/O 操作的基础类和几个支持特定种类的源和目标的I/O 操作的类组成。 10.2.1 流类库的基本类等级 在C+中,输入输出是通过流来完成的。C

25、+的输出操作将一个对象的状态转换成一个字符序列,输出到某个地方。输入操作也是从某个地方接收到一个字符序列,然后将其转换成一个对象的状态所要求的格式。这看起来很像数据在流动,于是把接收输出数据的地方叫做目标,把输入数据来自的地方叫做源。而输入和输出操作可以看成字符序列在源、目标以及对象之间的流动。C+将与输入和输出有关的操作定义为一个类体系,放在一个系统库里,以备用户调用。这个执行输入和输出操作的类体系就叫做流类,提供这个流类实现的系统库就叫做流类库。图10.1是简化的流类库的基本类等级图,而不是直接的继承关系图(不是UML图)。它们其实都是模板类,箭头仅仅是表示类的等级关系。,这个类等级在头文

26、件iostream.h 中说明。在图10.1中,ios类中的一个指针成员指向streambuf类的对象,streambuf类管理一个流的缓冲区,由于数据隐藏和封装的需要,普通用户一般不涉及streambuf 类,而只使用ios类、istream类和ostream 类中所提供的公有接口,进行流的提取和插入操作。ios类是istream类和ostream 类的虚基类,用来提供对流进行格式化I/O 操作和错误处理的成员函数。从ios类公有派生的istream和ostream两个类分别提供对流进行提取操作和插入操作的成员函数,而iostream类通过将istream类和ostream类组合起来以支持对

27、一个流进行双向(也就是输入和输出)操作,它并没有提供新的成员函数。 C+的流类库预定义了4个流,它们是cin、cout、cerr和clog。事实上,可以将cin视为类istream 的一个对象,而将cout视为类ostream的对象。 流是一个抽象概念,当实际进行I/O操作时,必须将流和,一种具体的物理设备,比如说键盘联接起来。C+的流类库预定义的4个流所联接起的具体设备为: cin 与标准输入设备相联接 cout 与标准输出设备相联接 cerr 与标准错误输出设备相联接(非缓冲方式) clog 与标准错误输出设备相联接(缓冲方式) 操作系统在默认情况下,指定标准输出设备是显示终端,标准输入设

28、备是键盘。在任何情况下,指定的标准错误输出设备总是显示终端。事实上,一直是通过键盘来使用cin ,通过显示器来使用cout的。但在某些场合,也可以将标准输入或标准输出设备指定为其他设备,比如说将文件操作转化为流的操作,简单方便。 在第1章已经介绍过C+同时提供的两种新的格式控制方式,但使用right或者left时,需要用setiosflags函数进行设置,毕竟不如“coutright;”之类的用法方便。本节将介绍使用ios_base类,以便简化操作。,10.2.2 默认输入输出格式控制 关于数值数据,默认方式能够自动识别浮点数并用最短的格式输出(如将5.0作为5输出,输入3.4e+2,输出34

29、0等)。还可以将定点数分成整数和小数部分。例如:int a; double b;cinaabc; 能将连续的3个字符分别正确地赋给相应对象。对字符串来讲,它从读到第一个字符开始,到空格符结束。对于字符数组,使用数组名来整体读入。假设声明“char a30;”则使用“cina;”读入,“couta;”输出。但对于字符指针,尽管为它动态分配了地址,也只能采取逐个赋值的方法,它并不以空格结束,但舍弃空格(读到字符才计数)。因为,字符串没有结束位,所以将字符串作为整体输出时,有效字符串的后面将出现乱码。不过,可以用手工增加表示字符串的结束符“0”来消除乱码。例如:char *p=new char5;f

30、or(int i=0;i*(p+i); /假设输入w e andfp4=0; /结束符coutbc; b3=0;,假如输入“we a”,则b内为we,c为a。输入“w e a”,则b内为w,c为e。 void不能声明对象,(void*)能输出无前缀的16进制数。例如整数a的值为35,则coutabcstrr;couta“ “b“ “c“ “str“ “rendl;,下面是部分输入和输出的对照,由此可以分析并看出它们的特点。键盘输入 输出结果 23.567 wty56.78 yu 23 0.567 w ty56.78 1 234 3647586.7897354 dfghe 0 234 3.647

31、59e+006 d fghe 0 456 3.4e+2 5 ty675 w 456 340 5 ty675 1如果默认输入输出格式不能满足自己的要求,就必须重载它们。,10.2.3 使用ios_base类 1. ios_base类简介 ios_base类派生ios类,ios类又是istream类和ostream类的虚基类。下面是ios_base类的部分定义。 class ios_base public:class failure;typedef T1 fmtflags;static const fmtflags boolalpha, dec, fixed, hex, internal,left

32、, oct, right, scientific, showbase, showpoint, showpos, skipws, unitbuf, uppercase, adjustfield, basefield, floatfield;typedef T2 iostate;,static const iostate badbit, eofbit, failbit, goodbit;typedef T3 openmode;static const openmode app, ate, binary, in, out, trunc; /其他部分fmtflags flags() const;fmt

33、flags flags(fmtflags fmtfl);fmtflags setf(fmtflags fmtfl);fmtflags setf(fmtflags fmtfl, fmtflags mask);void unsetf(fmtflags mask); /其他部分 protected: ios_base(); : fmtflags实际上就是整数类型,参数fmtfl为格式控制标志。常量名的含义如表10.1所示。,这些常量供成员函数ios_base : flags( ) 和 ios_base : setf(fmtflags) 来设置流的格式。格式标志存放在每个流的一个长整型成员中,每个标志

34、占1位,ios_base类中定义了如表10.2几个处理标志的成员函数。为了简单易懂,这里直接采取熟悉的数据类型表示它们。 使用流对象调用表10.2的成员函数。例如输出流cout调用设置精度的函数precision:cout.precision(4); 设置输出包含4位小数。这与1.4.5节的setprecision(int n)不同,那里的n连小数点在内。,2. 直接使用格式控制 表10.1中的名字可以直接用在系统提供的输入输出流中,而且有些是成对的。加no前缀表示取消原操作。下面给出对应符号(比表10.1的多),并用实例演示部分用法。 skipws showbase showpoint up

35、percase showpos untibuf boolalpha noskipws noshowbase noshowpoint nouppercase noshowpos nountibuf noboolalpha 【例10.1】演示使用标志位的例子。 #include using namespace std; const double PI=3.141592;,void main() int a=15; bool it=1, not=0;coutshowpoint123.0“ “ /输出小数位noshowpoint123.0“ “; /不输出小数位coutshowbase; /演示输出数

36、基couta“ “uppercasehexa“ “nouppercase /演示大小写hexa“ “noshowbaseadecaendl; coutuppercasescientificPI“ “nouppercasePI“ “fixedPIendl;coutcout.precision()“ ”PI“ ”; /演示/cout的成员函数,cout.precision(4);coutcout.precision()“ “PIendl;cout.width(10);coutshowposrighta“ “noshowposPI“ “; /演示数值符号coutit“ “not“ “boolalph

37、ait“ “ “not“ “ /演示boolnoboolalpha“ “it“ “notendl;cout.width(10);coutleftPI“ “123“ “cout.width()“ “;cout123“ “cout.width()endl; ,对照分析如下的输出结果是如何获得的,以便加深理解。 123.000 123 15 0XF 0xf f15 3.141592E+000 3.141592e+000 3.141592 6 3.141592 4 3.1416+15 3.1416 1 0 true false 1 0 3.1416 123 10 123 0 用width(int)设置

38、宽度的效果只对一次输入或者输出有效,在完成一次输入或输出之后,宽度设置自动恢复为0(表示按实际数据宽度输入输出)。在设置输入时,实际输入的字符串最大长度为n-1(宽度n计入结束符)。 setw(int)只对紧跟其后的输出有效。,3. 使用成员函数 在使用成员函数进行格式控制的时候,setf用来设置zj1格式控制,unsetf用来恢复默认设置。它们被流对象调用,使用表10.1的常量设置。例如,可以用它实现科学记数法、左对齐、带符号输出等功能。下面是对两个浮点数c 和d 使用格式输出的例子。 【例10.2】使用成员函数设置标志位的例子。 #include using namespace std;

39、const double PI=3.141592; void main() int a=15; cout.setf(ios_base:showpoint); /演示使用setf和unsetf,cout123.0“ “;cout.unsetf(ios_base:showpoint);cout123.0endl;cout.setf(ios_base:showbase);cout.setf(ios_base:hex, ios_base:basefield);couta“ “uppercasehexa“ “nouppercase /比较那种方便hexa“ “noshowbaseadec“ “aendl

40、;float c = 23.56F,d=-101.22F;cout.width(20);cout.setf(ios_base:scientific | ios_base:right | ios_base:showpos, ios_base:floatfield);cout c“t“ d“t“;,cout.setf(ios_base:fixed |ios_base:showpos, ios_base:floatfield);cout c“t“ dendl;coutcout.flags()“ “123.0“ “; /演示输出flagscout.flags(513); /演示设置flagscout1

41、23.0endl;cout.setf(ios_base:scientific); /演示省略方式cout123.0endl;cout.width(8); /设置填充字符数量(n-1)coutcout.fill(*)123endl; /演示填充 ,显然没有直接使用标识符方便。flags的设置较难,调用时要注意,fill和setf的定义也不一样。另外,有些参数可以省略,请与输出结果分析对比。输出结果如下: 123.000 123 0xf 0XF 0xf f 152.356000e+001 -1.012200e+002 23.559999 -101.220001 8705 123.000000 12

42、3 1.230000e+002 * 123,10.3 文件流 无论使用哪一种语言,要做实际的工作,就一定会和文件打交道,因为大多数情况下都是通过文件来存取数据的。在C+里,文件操作是通过流来完成的。系统通过对流类进一步扩展,提供了支持文件的能力,这使得程序员在建立和使用文件时,就像使用cin和cout一样方便。 图10.4表示扩展处理文件的类等级,新派生的5个类用于文件处理。 在使用文件之前,必须弄清楚要对该文件进行哪些操作,这直接关系到使用哪一种文件流。C+总共有输入文件流、输出文件流和输入输出文件流3种,并已经将它们标准化。要打开一个输入文件流,定义一个ifstream类型的对象就可以了;

43、同样,要打开一个输出文件流,也需要定义一个ofstream类型的对象;如果要打开输入输出文件流,就要定义一个fstream 类型的对象。这3种类型都是在头文件 fstream.h里定义的,别忘记包含它。它们的类等级图可以参考有关文献8,基类是ios_base。,filebuf类是streambuf的派生类,它提供了对文件缓冲区管理的能力,一般不涉及这个类。fstreambase类提供了文件处理所需要的全部成员函数,在它的派生类中没有提供新的成员函数。这几个类也同时继承了前面介绍的流类库的基本类等级中定义的成员函数。,10.3.1 文件流的概念 使用流类建立和读写文件时,就像使用cin和cout

44、一样方便。以写文件为例,假如建立一个类似cout的输出文件流myFile,就可像使用cout一样,使用myFile将指定内容写入文件。只要myFile也是ofstream类型的对象,即可以实现这一操作。 与cin接受输入的方法一样,只要建立一个类似cin的输入文件流getText,就可像使用cin一样,使用getText读取文件的内容。这时,getText应该是ifstream类型的对象。 【例10.3】演示文件流的概念。 #include #include using namespace std;,void main() char ch15, *p=“abcdefg“;ofstream my

45、File; /建立输出流myFilemyFile.open(“myText.txt”); /建立输出流myFile和/文件myText.txt之间的关联myFilep; /使用输出流myFile将字符串流向文件myFile“GoodBye!”; /使用输出流myFile直接将/字符串流向文件myFile.close(); /关闭文件mfText.txtifstream getText(“myText.txt“); /建立输入流getText/及其和文件myText.txt的关联,/使用输入流getText每次从文件myText.txt读入1个字符for(int i=0; ichi; /将每次读

46、入的1个字符赋给/数组的元素chichi=0; /设置结束标志getText.close(); /关闭文件mfText.txtcoutch; /使用cout流向屏幕 程序写入文件myText.txt的内容为“abcdefgGoodBye!”,然后再从文件读出来,显示在屏幕上。其实,可以直接打开文件myText.txt进行验证。 可以总结出对文件进行操作的方法如下: 打开一个相应的文件流。例如打开一个输出文件流 myStream:ofstream myStream;,(2) 把这个流和相应的文件关联起来。例如语句:myStream.open(“myText.txt“); 就通过open函数把文件

47、流myStream和文件myText.txt关联起来了,以后对文件流myStream的操作,就是对文件myText.txt的操作。 因为ifstream、ofstream和fstream 这3个类都具有自动打开文件的构造函数,而这个构造函数就具有open()的功能。因此,事实上可以用一条语句ofstream myStream(“myText.txt“); 来完成上述两步,在【例10.3】中,对输入文件流就是直接使用这种方法。如果指定文件路径,路径中的“”号必须使用转义字符表示。例如:ifstream getText(“f:textfilemyText.txt“); 表示的全路径文件名为:f:t

48、extfilemyText.txt。,(3) 操作文件流。想对文件进行什么操作,对相应的文件流进行该操作就行了。例如可以像cout那样,使用put或者write成员函数对输出流进行操作;可以像cin那样,使用get、getline或者read成员函数对输出流进行操作。不过,不要忘了及时关闭不再使用的文件。关闭该文件流的 方法很简单,格式为:文件流名.close(); 流是I/O流类的中心概念。流是一种抽象,它负责在数据的生产者和数据的消费者之间建立联系并管理数据的流动。程序将流对象看做是文件对象的化身。 一个输出流对象是信息流动的目标,ofstream是最重要的输出流。一个输入流对象是数据流动的源头,ifstream是最重要的输入流。一个iostream对象可以是数据流动的源或目的,fstream是从它派生的。下面简要介绍它们的流成员函数,详细说明需参考有关类库的手册。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报