C的IO流库-首都师范大学.ppt_第1页
C的IO流库-首都师范大学.ppt_第2页
C的IO流库-首都师范大学.ppt_第3页
C的IO流库-首都师范大学.ppt_第4页
C的IO流库-首都师范大学.ppt_第5页
已阅读5页,还剩144页未读 继续免费阅读

下载本文档

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

文档简介

第八章 C+ 的 I/O 流库,C+ 为什麽要建立自己的输入输出系统? 1 C 虽然具有一个灵活和功能强大的输入输出系统, 但它并不支持自定义类型。例如: class account char name30; double balance; public: account(); account(char*, double); ;, account acnt; scanf(“%account”, / 错误,不支持 account 类型 因为输入函数 scanf 和输出函数 printf 的格式串形参 只能与系统预定义类型匹配,而无法识别用户的自 定义类型,并且也不能通过重载定义 scanf 和 printf 函数的新版本,使它们的格式串形参能匹配任意用 户自定义类型。,2 面向对象程序设计必须定义众多的用户自定义类, 如何以面向对象的设计原则和方法为自定义类设计 既规格统一,又适应个性化的输入输出操作行为是 十分必要的。因此 C+ 必须建立一个能通过对输入 输出操作重载的方法实现对任意自定义类型对象输 入输出支持的系统。,本章要点 1 C+ 流库结构 流库的概念、流库的组成。 2 标准输入输出流 输入输出流类的定义、输入输出运算符、输入输出 的格式控制。 3 自定义类的输入输出 输入输出运算符的重载。 4 文件的输入输出流 文件的打开、关闭和读写。 5 使用 MFC 的对话框类实现输入输出,8.1 C+ 流库结构 8.1.1 流库的概念 流(stream)是从源(数据的生产者)到目标(数 据的使用者)被传输数据的引用。每个流都是一个与 某种数据传输设备相关联的对象。 流具有方向性: 输入流是与输入设备(如键盘)关联的流。 输出流是与输出设备(如显示器屏幕)关联的流。 输入输出流是与输入输出设备(如磁盘)关联的流。,C+ 中包含的预定义流: cin 输入流,与输入设备关联。 cout 输出流,与输出设备关联。 cerr 非缓冲型错误信息流,与错误输出设备关联; clog 缓冲型错误信息流,与错误输出设备关联。 在缺省情况下,指定的输入设备是控制台键盘,输 出设备是控制台显示器终端。在任何情况下,指定的 错误输出设备总是控制台显示器终端。,cin 和 cout 的使用方法我们已经很熟悉了。cerr 和 clog 均是用来输出错误信息,它们的使用方法与 cout 基本相同,只不过它们所关联的设备始终是控制台显 示器,而不随着 cout 关联设备的改变而变化。cerr 和 clog 之间的区别是: cerr 对输出的错误信息不缓冲,因而发送给它的任何内 容都立即输出。 clog 输出的错误信息被缓冲,当缓冲区满时才进行输 出,也可以通过刷新流的方式(遇到操纵符 endl 或 flush)强迫刷新缓冲区导致显示输出。,下面给出一段使用预定义输入输出流信息的程序: cout sales; cout num; if (num = 0) cerr “The average can not be computed.n“; else avgsales = sales / num; cout “The average selling price per nuit was “; cout avgsales “n“; ,C+ 流库是用面向对象的设计方法建立起来的输入 输出类库,它具有两个平行的根基类 streambuf 和 ios, 库中所有其他的类均从它们直接或间接派生。系统中 预定义流,cin、cout、cerr、clog 都是流库中相应类的 对象。,8.1.2 streambuf 类 streambuf 类是流库的根基类,它为输入输出物理设 备提供缓冲区和流处理的一些通用方法。 C+ 将输入输出流均视为字节流,因此缓冲区是由 一个字符串和两个指针组成的。这两个指针分别指 向数据流在输入缓冲区中的插入位置和在输出缓冲 区的提取位置。 streambuf 类提供对缓冲区的底层操作,例如设置缓 冲区、对缓冲区指针进行操作、从缓冲区取字节、向 缓冲区存储字节等。streambuf 类有三个派生类,filebuf 类、strstreambuf 类和 conbuf 类。它们的派生层次关系 如图所示:, filebuf 类扩展了 streambuf 类的功能,用于文件流与 文件缓冲区相关联,实现对文件缓冲区中的字节序 列的读写操作: 写文件:缓冲区内容按字节写到指定的文件中, 然后刷新缓冲区。 读文件:指定文件内容按字节读到缓冲区中。 打开文件:filebuf 与被读或写的文件相关联。,streambuf, 关闭文件:filebuf 与被读或写的文件解除关联。 strstreambuf 类扩展了 streambuf 类的功能,提供了将 内存作为输入输出设备时,进行提取和插入操作的 缓冲区管理。 conbuf 类扩展了 streambuf 类的功能,用于控制台显 示器输出缓冲区关联和管理,提供光标控制、颜色 设置、活动窗口定义、清屏、行清除等屏幕控制功 能(注意,这些屏幕控制功能在有些编译器版本中 不提供,例如 visual C+)。 通常情况下,对设备缓冲区的操作一般使用上述三 个派生类,很少直接使用基类 streambuf。,8.1.3 ios 类 ios 类及其派生类为用户提供使用流的接口,该类具 有一个 streambuf 类型指针指向流的缓冲区。ios 类及其 派生类对象通过该指针使用 streambuf 及其派生类对象 完成输入输出时的格式化或非格式化转换,并检查输 入输出的错误。ios 是流库中的另一个根基类,它有四 个直接派生类: 输入流类 istream: class istream : virtual public ios; 输出流类 ostream: class ostream : virtual public ios;, 文件流类 fstreambase: class fstreambase : virtual public ios; 字符串流类 strstreambase: class strstreambase : virtual public ios; 这四个派生类成为流类库中的基本流类,它们又组 合派生出以下的流类: 输入文件流类 ifstream: class ifstream : public istream, public fstreambase; 输入字符串流类 istrstream: class istrstream : public istream, public strstreambase; 输出文件流类 ofstream: class ofstream : public ostream, public fstreambase;, 输出字符串流类 ostrstream: class ostrstream : public ostream, public strstreambase; 控制台输出流类 constream: class constream : public ostream; 输入输出流类 iostream: class iostream : public istream, public ostream; 输入输出文件流类 fstream: class fstream : public iostream, public fstreambase; 输入输出字符串流类 strstream: class strstream : public iostream, public strstreambase;,从上述的 istream、ostream 和 iostream 又分别派生出 具有赋值运算符“=”重载的新类: 带赋值的输入流类 istream_withassign: class istream_withassign : public istream; 带赋值的输出流类 ostream_withassign: class ostream_withassign : public ostream; 带赋值的输入输出流类 iostream_withassign: class iostream_withassign : public iostream; 上述流类的派生层次结构如下图所示。,ios,系统预定义流 cin、cout、cerr 和 clog 在系统头文件 iostream.h 中被定义: extern _CRTIMP istream_withassign cin; extern ostream_withassign _CRTIMP cout; extern ostream_withassign _CRTIMP cerr; extern ostream_withassign _CRTIMP clog; 显然,用户也可以用 istream 和 ostream 等流类定义 自己的流对象,例如:istream is; ostream os; 对预定义类型数据的输入输出操作已经定义为流类 的操作,而对用户自定义类型对象的输入输出操作则 可以通过重载运算符 “”和“” 得以实现。,Java 的 I/O 流类在构造和使用上与 C+ 有一些有趣 的差别: 1 C+ 的 I/O 流类库是把所有的输入输出功能封装在 数量相对较少的几个流类中,自定义类则是通过重 载机制使得流类对象能够对自定义类对象进行输入 输出操作。 而 Java 则几乎为每一种情况提供了一个独立的类。 例如,底层的字节流操作类、按数据类型划分的各 种高级操作类,对底层 I/O 包装后进行某种特定操 作(如 Unicode 读写)的类,随机读写操作类,数 组读写类等等。,2 C+ 的流在缺省情况下是进行缓冲的,但 Java 的流 在缺省情况下是不缓冲的。但可以调用提供了缓冲 功能的类对一个 Java 的流进行缓冲。 3 在 Java 中,所有的整数和浮点数都是以大端字节顺 序进行输入输出的,与底层平台无关。这与 C+ 中 的情况不同,因此 Java 所产生的数据文件的可移植 性更高。,4 在典型的 C+ 程序中,字符均使用 ASCII 编码形式 表示(无论是单独使用的字符还是字符串中的字 符)。这与字符从输入设备读入或写到输出设备都 保持 ASCII 编码形式是一致的。 一个典型的 Java 程序中,字符始终是用两个字节的 Unicode 形式表示的。当从输入设备读入 ASCII 编码 形式的字符时,必须先转换为 Unicode 形式后再写 入内存;当将 Unicode 形式字符写到输出设备时, 必须先转换为 ASCII 编码形式后再写入输出设备。 Java 的流类的层次结构如下图所示:,OutputStream,InputStream,返回,Writer,Reader,8.2 标准输入输出流 8.2.1 输入输出流类定义 istream 类和 ostream 类在流库中分别提供基本的标 准输入输出操作,是使用流库的主要接口,在系统头 文件 iostream.h 中,它们的类定义分别如下: class istream:virtual public ios public: istream(streambuf*); /构造函数 / 从输入流中将字符读取到给定指针 char* 指向的内存,直 / 至遇到分界符、文件结束符或读至(len-1)个字符为止。 istream,/ 从输入流读取字符到给定的 streambuf,直至遇到分界符。 istream,/ 从读入流读取给定数目的字符到 char* 指向的内存空间。 / 如果发生错误时,getcount 函数可得到实际读入的数目。 istream,/ 按给定偏移 streamoff ( typedef long streamoff)移动输入 / 文件指针,指示偏移方向的枚举值 ios:seek_dir 的取值: / beg = 从文件开始;cur = 从当前位置;end = 从文件尾。 istream /对系统所有的预定义类型都给出了的重载定义。 ;,class ostream:virtual public ios public: ostream(streambuf*); / 构造函数 ostream,streampos tellp(); / 返回输出流中当前指针位置。 / 可重载的输出运算符 ostream,8.2.2 输入输出运算符的使用 8.2.3 格式控制的输入输出 输入输出的格式控制可以使得人机交互界面更加友 好、美观。在 C 程序中,输入输出是使用 C 运行库的 scanf 和 printf 函数完成的,输入输出的格式是通过这两 个函数的格式描述串参数控制的。在 C+ 程序中,虽 仍然可以使用 scanf 和 printf 函数,但在面向对象的程 序设计中,输入输出是使用流类库中的输入输出流完 成的,因此输入输出的格式控制必须使用流类库提供 的格式控制的方法: 使用 ios 类的格式控制成员函数; 使用被称为格式操纵符的特殊函数。,1 用 ios 类成员函数进行输入输出格式化 ios 类提供了用于输入输出格式控制的成员函数。 这些函数进行格式控制的方法是修改以下属性: 格式标志属性 x_flags:标志的不同状态值指定输 入输出数据的不同格式(如对齐规则、数值转换 基、数字表示规则等)。 输出域宽属性 x_width: 指定输出数据所占显示 区域的宽度。 填充字符属性 x_fill:指定输出显示域中数据为占 空间的填充字符; 输出精度属性 x_precision:指定浮点数输出的小 数部分显示位数。, 格式标志 C+ 中每个流对象的输入输出格式都是依据指定 格式进行的,也就是说,流对象的每次输入“” 操作是按照当前格式标志 x_flags 中 的格式状态完成的。该属性是一个 protected 成 员,在类外访问该属性是通过公有的格式控制成 员函数实现的。注意,该属性可以在 ios 的派生 类内被访问。为了便于提供格式控制成员函数的 参数和参数具有良好的可读性,ios 类定义了一 个公有的无名枚举数据成员,用户可以使用这些 特定的枚举元素,形成所需要的格式状态传递给 相应的格式控制成员函数。,class _CRTIMP ios public: enum skipws = 0x0001, / 跳过输入中的空白,用于输入 left = 0x0002, / 左对齐输出,用于输出 right = 0x0004, / 右对齐输出,用于输出 internal = 0x0008, / 符号或基数指示符与数字之间添加填充符,用于输出 dec = 0x0010, / 基数为 10 进制,用于输入输出 oct = 0x0020, / 基数为 8 进制,用于输入输出 hex = 0x0040, / 基数为 16 进制,用于输入输出,showbase = 0x0080, / 显示基数指示符,用于输出 showpoint = 0x0100, / 显示小数点,用于输出 uppercase = 0x0200, / 16 进制输出时,数基指示符和 / 数值中的字母一律为大写,用于输出 showpos = 0x0400, / 正数前显示“+“符号,用于输出 scientific = 0x0800, / 科学表示法浮点数,用于输出 fixed = 0x1000, / 定点形式显示浮点数,用于输出 unitbuf = 0x2000, /输出后立即刷新流,用于输出。 stdio = 0x4000, / 刷新 stdout 和 stderr,用于输出。 ; inline long flags() const; / 返回当前格式标志。,inline long flags(long _l); / 设置指定格式 _l,返回原有格式。 inline long setf(long _f, long _m); / 依据掩码 _m 设置指定格式 _f,返回原有格式。inline long unsetf(long _l); / 清除指定格式 _l,并返回原有标志。 inline int width() const; / 返回当前域宽值。 inline int width(int _i); / 设置指定域宽 _i,并返回原有域宽值。 inline ostream* tie(ostream* _os); / 将流连接到_os 指向输出流,并返回原来的流指针。 inline ostream* tie() const; / 返回原来的流指针。,inline fill() const; / 返回当前填充字符。 inline char fill(char _c); / 设置指定填充字符,并返回原有填充字符。 inline int precision(int _i); / 设置指定浮点数精度,并返回原有浮点数精度。 inline int precision() const; / 返回当前的浮点数精度。 inline int rdstate() const; / 返回当前的错误状态。 inline void clear(int _i = 0); / 根据掩码 _i 设置或清除错误状态位。 protected: ,long x_flags; int x_precision; char x_fill; int x_width; ;,格式枚举元素值有一个共同的特点,即使用不同 位为 1 二进制数表示不同的格式值,也就是说, 枚举元素值的二进制表示只有一位为 1。例如: skipws 0x0001: 0000 0000 0000 0001 left 0x0002: 0000 0000 0000 0010 right 0x0004: 0000 0000 0000 0100 显然,所需要的特定格式标志将可以是一个枚举 元素值或几个的枚举元素进行或运算组合而成, 例如,欲设置左对齐 10 进制科学表示法显示浮 点数的输出格式,则格式标志可以通过 ios:left | ios:dec | ios:scientific 得到,16 进制值为 0x0812,10 进制值为 2066。, 用成员函数对格式标志进行操作 置格式标志 所谓设置格式标志是将格式属性 x_flags 的某 一位置1,使该位所对应的格式标志有效。设 置格式标志的成员函数是 setf,调用该成员函 数的一般形式为: 流对象.setf (ios:格式标志值); 例如: istream isobj; ostream osobj; isobj.setf(ios:skipws); osobj.setf(ios:left);,注意: isobj 和 osobj 为 istream 和 ostream 的用户定 义对象。在编程中使用最多的是通过系统预 定义流对象设置格式,例如: cin.setf(ios:skipws); cout.setf(ios:left); 所设置的格式标志不改变格式属性 x_flags 的 原有的有效位,即在原有基础上追加设置。 例如,原来的状态标志字为: 0x0011: 0000 0000 0001 0001 执行 cout.setf(ios:left) 后, x_flags 的值变为: 0x0013: 0000 0000 0001 0011,例如: #include main() cout.setf(ios:showpos | ios:scientific); cout 567 “ “ 567.89 endl; 程序执行结果: +567 +5.6789e02, 清除格式标志 与设置格式标志操作相反,是将格式属性 x_flags 的某一位清 0,使该位所对应的格式特 性失效。清除格式的成员函数是 unsetf,调用 该成员函数的一般形式为: 流对象.unsetf(ios:格式标志值); 注意:与设置格式标志相似,所清除的格式标 志只是使保存在 x_flags 中的当前格式属性的 相应位失效,而不改变格式属性其余的有效 位。例如,原格式属性为:,0x0013: 0000 0000 0001 0011 执行 cout.unsetf(ios:left) 后,x_flags 的值变为: 0x0011: 0000 0000 0001 0001, 取状态属性 取出保存在 x_flags 中的格式属性。完成这一 操作的成员函数 flags,该函数有两个重载版 本,调用它们的格式有两种: long flags(); long flags(long flags); 前一种形式用于返回当前格式属性;后一种是 不仅将当前格式属性返回,并且将格式属性设 置为指定值 flags。 注意,带有参数的成员函数 flags 与成员函数 setf 不同,它对格式属性的修改是覆盖原值, 而不是在原值的基础上追加设置。例如:,#include void showflags(long f) / 显示二进制形式的状态字 long i; for (i = 0x8000; i; i = i 1) if (i ,main() long f = cout.flags(); showflags(f); cout.setf(ios:showpos | ios:scientific); f = cout.flags(); showflags(f); cout.unsetf(ios:scientific); f = cout.flags(); showflags(f); f = cout.flags(ios:oct); showflags(f); f = cout.flags(); showflags(f); return 1; ,程序执行结果: 0000000000000000 0000110000000000 0000010000000000 0000010000000000 0000000000100000 分析程序的执行结果,可以清楚地看到格式属 性值的变化情况。, 设置域宽 域宽主要用来控制一个数据输出时所占显示区 域的宽度,在 ios 类中,域宽存放在保护类数 据成员 x_width 中。设置域宽的成员函数有两 个,调用它们的一般形式为: 流对象.width(); 流对象.width(域宽值); 第一种形式只用来返回当前的域宽值,后者用 来设置指定域宽,并返回原来的域宽值。, 设置显示的精度 在 ios 类中,控制浮点数显示精度位数是被保 存在保护类数据成员 x_precision 中的。设置显 示精度的成员函数有两个,调用它们的一般形 式为: 流对象.precision(); 流对象.precision(精度位数); 第一种形式只用来返回当前的显示精度,后者 用来重新设置显示精度,并返回原来的显示精 度。, 设置填充字符 填充字符的作用是:当输出数据的长度小于显 示域宽时,用填充字符来填充显示域宽中数据 未占满的空间。缺省情况下填充字符为空格。 如果输出数据的长度大于域宽时,则填充字符 是没有意义的。因此,在使用填充字符时,必 须考虑与 width 函数相配合。在 ios 类中,填 充字符被保存在保护类数据成员 x_fill 中的。 设置填充字符的成员函数有两个,调用它们的 一般形式为:,流对象.fill(); 流对象.fill(填充字符); 第一种形式用来返回当前的填充字符,后者用 来重新设置填充字符,并返回原有的填充字 符。例如:,#include main() cout “x_width = “ cout.width() endl; cout “x_fill = “ cout.fill() endl; cout “x_precision = “ cout.precision() endl; cout 123 “ “ 123.45678 endl; cout “-n“; cout “* x_width = 10, x_fill = , x_precision = 8 *n“; cout.width(10); cout.precision(8); cout 123“ “123.45678“ “234.567 endl;,cout “x_width = “ cout.width() endl; cout “x_fill = “ cout.fill() endl; cout “x_precision = “ cout.precision() endl; cout “-n“; cout “* x_width = 10, x_fill = ,cout “x_fill = “ cout.fill() endl; cout “x_precision = “ cout.precision() endl; return 1; 程序执行结果: x_width = 0 x_fill = x_precision = 6 123 123.457 -,* x_width = 10, x_fill = , x_precision = 8 * 123 123.45678 234.567 x_width = 0 x_fill = x_precision = 8 - * x_width = 10, x_fill = &, x_precision = 8 * &123 123.45678 123& 123.45678 x_width = 0 x_fill = & x_precision = 8,分析程序的执行结果,有几点需要注意: 在缺省情况下,x_width 的值为 0,即无域 宽,这种情况下输出数据按自身的宽度显示; x_fill 的值为空格;x_precision 值为 6,因此, 浮点小数 123.45678 只被显示了 6 位,并且被 四舍五入。 用 width 函数设置的域宽只对紧跟它的数据输 出起作用,随后 x_width 立即自动置为0。而用 precision 函数设置了 x_precision 和用 fill 函数设 置了 x_fill 后,将一直有效,直至它们被重新 设置。, 当设置了 x_precision 后,若实际输出数据的精 度与其不一致时,将遵循如下的规则: 当实际输出的浮点数位大于 x_precision 时, 则以 x_precision 的位数按四舍五入输出; 当实际输出的浮点数位小于 x_precision 时, 则按实际的小数位数输出; 当 x_precision 为 0 时,x_precision 将被缺省 设置为 1。, 当显示数据所需要的宽度比所设置的域宽小 时,数据未占的显示域部分用填充字符来填 充,缺省的填充字符为空格。字符的填充位 置由 ios:left 和 ios:right 确定。 例8-1 使用 ios 的成员函数控制输出格式显示一个平方 根和平方表。,2 使用操纵函数(符)进行格式化控制 不难发现,如果希望使用格式成员函数控制输入输 出格式,而不输出这些成员函数的返回值时,则函 数的调用表达式必须是一条单独的语句,而不能嵌 入到输入输出流表达式中去。C+ 流类库提供了另 一种进行输入输出控制的方法,这种方法使用一种 被称为操纵符的特殊类外函数。由于对操纵符的调 用表达式可以嵌入到输入输出流表达式中,所以用 操纵符控制格式比用格式成员函数更方便。, 流类库中预定义的操纵符 操纵符是一个特殊类外函数,该函数是以一个 “流引用” 作为参数并将由参数传递来的 “流引 用” 作为函数的返回。操纵符的一般原型为: 流类 可以认为操纵符作用是将一个输入流或输出流经 过指定的格式处理后又传递给下一个需要对输入 流或输出流进行的操作。因此操纵符像一个起到 “接力” 作用的 “运算符”,可以被嵌入到输入输出 流表达式中。预定义操纵算子如下表所示:,操纵符分别被定义在头文件 ios.h、istream.h、 ostream.h 和 iomanip.h 中: inline _CRTIMP ios ,inline _CRTIMP ostream ,inline ios 注意:这些操纵符函数不是相应类的成员函数, 而是仅对输入输出流有效的特殊类外函数。 操纵符的使用 在 C+ 中,输入输出流操作是通过使用输入运算 符 “” 和输出运算符 “ 是流类 istream 的成员运算符函数,它在类说明 中被说明为:,class _CRTIMP istream : virtual public ios public: inline istream,istream,显然,“” 的众多重载定义是为了满足输入各种 预定义类型数据和控制的需要。这些重载函数的 共同点是返回类型和缺省的左操作数均为输入流 的引用 istream&,而不同点是它们的右操作数各 异,其中以 ios& 和 istream& 为右操作数的重载版 本是用于控制输入格式。 而 “” 是流类 ostream 的成员运算符函数,它在 类说明中被说明为:,class _CRTIMP ostream : virtual public ios public: inline ostream,ostream,同样,“” 的众多重载定义是为了满足输出各种 预定义类型数据和控制的需要。这些重载函数的 共同点是返回类型和缺省的左操作数均为输出流 的引用 ostream&,而不同点是它们的右操作数各 异,其中以 ios& 或 ostream& 为右操作数的重载 版本是用于控制输出格式。,操纵符正是通过使用用于输入格式控制的 “ 重载版本或用于输出格式控制 “” 或 “” 或 “” 或 “” ,使后续执行的输入 或输出操作受到所设置格式的控制。下面再通过 两个实例来进一步理解操纵符的使用。,#include main() cout setw(10) 123 567 endl; cout 123 setiosflags(ios:scientific) setw(20) 123.456789 endl; cout 123 setw(10) hex 123 endl; cout 123 setw(10) oct 123 endl; cout 123 setw(10) dec 123 endl; cout resetiosflags(ios:scientific) setprecision(4) 123.456789 endl; cout setiosflags(ios:left) setfill(#) setw(8) 123 endl;,cout resetiosflags(ios:left) setfill( 程序执行结果: 123567 123 1.234568e+002 123 7b 7b 173 173 123 123.5 123# &456,分析程序执行结果,有两点需要注意: 操纵算子 setw 与 ios:width 相似,它的作用是 一次性的,即只对最近的下一次输出起作用。 此程序中的小数为浮点数表示,如果将小数 的表示设置为定点表示,则会使科学计数表示 法失效。 例8-2 显示 1 20 的二次方表与二次方根表程序 的另一个版本。这一版本中使用输入输出 操纵算子代替 ios 类的格式控制成员函 数。, 用户自定义的操纵符 C+ 流类库除了提供预定义的操纵符外,也允许 用户利用操纵符的定义机制自定义操纵符。至 此,我们应该已经深深地感觉到 C+ 系统自身的 许多实现机制常常可以被用户使用,使 C+ 的使 用者有一种与系统融合的感受。 用户自定义操纵符的目的往往是归纳程序中频繁 使用的输入输出格式操作,提高输入输出代码的 重用度,程序的可读性和可维护性。建立自定义 操纵符的方法:,若为输出流定义操纵符函数,则定义形式如下: ostream ,例如: #include ostream ,程序执行结果: 123 7B 又例如: #include istream ,int main() int i; cin input i; cout i “ “ output i endl; return 1; 程序执行结果: Enter number using hex format: a78e 42894 A78E 返回,8.3 自定义类型的输入输出 从前面对 C+ 流类库的分析不难看出,系统预定义 类型数据的输入输出主要是通过使用输入运算符“ (又称为流的提取运算符)和输出运算符 “ 和输出运算符 “ 是无法预先定义的。但却 为用户提供了使用运算符重载为自定义类型对象定义 输入输出操作的面向对象方法。解决自定义类型对象 输入输出操作的思路和方法有三种:,1 用户自定义类型总可以分解成按指定结构组织起来 的预定义类型,对这组预定义类型数据当然可以使 用 “ 或 “ 运算符。使用这种方法的缺点: 在类外访问被分解得到的预定义类型数据必须通 过接口,必然降低访问效率。如果类数据成员均 定义为公有访问属性,虽然能提高访问效率,但 破坏了面向对象的数据隐藏原则。显然不能兼顾 访问效率和数据安全。 无法利用代码的重用性,增加了程序冗余度; 程序的可读性差;,2 在方法 1 的基础上建立用户自定义类的特定输入输 出成员函数。采用这种方法虽然解决了采用方法 1 的缺点,但仍然存在以下缺点: 增加了需要记忆的特定的输入输出成员函数名, 并在调用这些函数时,需在函数名前加缀对象 名,因此增加了编程的复杂性。 这些函数只能单独使用(写成一个单独语句), 而不能放在输入或输出连续表达式中,无法统一 输入输出操作的形式。 3 采用重载 “ 和 “ 运算符的方法,并将运算符 函数定义成被输入或输出对象类的友元函数。这种 方法将会消除前两种方法所带来的缺点,是一种遵 循面向对象设计原则的方法。,8.3.1 重载输入运算符 “ 输入运算符 “ 是输入流类(将在第八章中详细讲 述)的成员函数。该运算符也是一个双目运算符,它 的左操作数必须是输入流类对象的引用,表示被输入 的信息必须来自标准的输入流设备;而右操作数是接 收输入信息的指定类对象的引用。因此,为自定义类 重载的输入运算符函数只能是类成员函数。,1 原型 friend 输入流类,2 定义 输入流 ,3 调用 cin 类对象名; 例如: point pt; cin pt;,注意: 输入运算符重载函数的第一个参数的类型必须是输 入流类 istream 对象的引用,形参名(流对象名)可 以使用任何合法的标识符。 输入运算符重载函数的第二个参数的类型必须是接 收输入信息的指定类对象的引用,例如 point&, 而 不能使用指定类名,例如 point。 输入运算符重载函数的返回类型必须是输入流类 istream 对象的引用,并且在函数体中由 return 返回 的输入流类对象的引用名必须与第一个参数的形参 名(流对象名)一致。,8.3.2 重载输出运算符 “ 输出运算符 “ 是输出流类(将在第八章中详细讲 述)的成员函数。该运算符也是一个双目运算符,它 的左操作数必须是输出流类对象的引用,表示信息必 须被输出到标准的输出流设备;而右操作数是输出信 息的指定类对象。因此,为自定义类重载的输入运算 符函数也只能是类成员函数。,1 原型 friend 输出流类,2 定义 输出流 ,3 调用 cout pt; cout pt;,注意: 输出运算符重载函数的第一个参数的类型必须是输 出流类 ostream 对象的引用,形参名(流对象名)可 以使用任何合法的标识符。 输出运算符重载函数的第二个参数的类型必须是输 出信息的指定类对象或对象的引用,例如 point&, 或 point。 输出运算符重载函数的返回类型必须是输出流类 ostream 对象的引用,并且在函数体中由 return 返回 的输出流类对象的引用名必须与第一个参数的形参 名(流对象名)一致。,例8-3 中对输入运算符 和输出运算符 和输出运算符 进行了重 载,使它们能对按照指定形式(例如由分子分母 组成的分数形式,3/8) 表示的有理数进行标准 输入输出操作。 返回,8.4 文件的输入输出流 文件一般是指存储在计算机的外部介质(磁盘、光 盘、U盘等)上,按指定的结构组织起来的数据集合。 操作系统就是以文件为单位对数据进行管理的。对文 件的输入输出操作是程序设计的重要概念,是数据输 入输出流的又一重要形式。因此,C+ 的流类库把文 件也按照字节序列对待和处理,但组成文件的字节在 不同类型的文件中具有不同的含义,例如:它们可以 是字母、符号,也可能是数值。根据文件所保存的数 据组织形式,文件可以分为两类:,1 文本文件:又称为 ASCII 文件,这类文件中的每个 字节中存放一个 ASCII 代码,其含义一个字符。因 此,如果数据在内存中存放形式是 ASCII 代码,则 这样的数据无论是输出到文本文件还是从文本文件 输入都不需要进行任何变换;但如果数据在内存中 存放形式是二进制数,则这样的数据输出到文本文 件时必须先变换为 ASCII 代码,而从文件输入时必 须先变换为二进制数。例如整数 100000(0x186A0) 在内存中(二进制数)和文本文件中的存放形式:,显然,这样的输出过程和数据表示形式的转换与内 存中的二进制数据输出到控制台显示器是一致的, 即先将二进制数据转换为 ASCII 代码,在传送到显 示器的显示存储中,从而得以在显示器屏幕上以字 符形式显示输出。而输入过程和数据表示形式的转 换与从控制台键盘输入数据,并以二进制形式存放 到内存是一致的,即先将键盘输入产生的 ASCII 代 码转换为二进制形式后,传送到指定内存单元中。 使用文本文件存放和处理数据的特点: 优点:直观、方便、可读。 缺点:占用空间多,数据转换开销大。,2 二进制文件:这类文件中的每个字节存放二进制数 据字节,其含义视数据表示的内容(文本字符、计 算数据值、图象象素值等)而定。因此,无论数据 在内存以何种形式存放,输入输出到二进制文件中 的存放形式都保持不变,即无须进行任何转换。例 如,同样是整数 100000(0x186A0)在内存中(二进 制数)和二进制文件中的存放形式: 使用二进制文件存放和处理数据的特点: 优点:占用空间少,无数据转换开销,保密性好。 缺点:不直观,不方便,不可读。,无论是文本文件还是二进制文件,总可以把存放在 文件中数据看作是一连串的字节记录,而不考虑记录 的界限,对文件的存取都是以字节为单位进行的。我 们把这种文件成为流式文件。 对一个流式文件进行存取操作之前,必须首先创建 一个流,然后建立流与文件的关联,即打开文件,完 成流的插入或提取操作后,关闭这个文件,即解除流 与文件的关联。,8.4.1 文件的打开与关闭 所谓 “打开” 文件就是一个文件与一个流建立关联。 只有一个打开的文件才能够通过流对其进行输入输出 操作。为了执行对文件的输入输出操作,C+ 流类库 中有三个提供了文件读写操作的流类,从它们的类派生层次中可以看出:根基类 ios 和 istream 或 ostream 中定义的操作在这三个相应的派生流 类中均可以使用。这三个类被定义在头文件 fstream.h 中。执行文件输入输出,必须做以下三件事: 在程序中必须包含系统头文件 fstream.h。 建立流 创建文件流类的对象,例如: ifstream in; / 输入流对象 ofstream out; / 输出流对象 fstream io; / 输入输出流对象,使用文件流类的成员函数 open 打开文件,也就是 使某一个文件与指定流相关联。open 的原型: void open( const char* szName, int mode, int nProt = filebuf:openprot ); 其中三个参数分别为: 1 文件名:字符串常量 szName 用来传递文件名。 2 打开方式:整型值 mode 指定了文件被打开的方 式,其取值范围如下表所示:,在 ios 类定义中 mode 值被定义为以下枚举: enum open_mode in = 0x01, out = 0x02, ate = 0x04, app = 0x08, trunc = 0x10, nocreate = 0x20, noreplace = 0x40, binary = 0x80 ;,各个枚举值所指定的打开方式的详细含义: 追加方式 ios:app: The function performs a seek to the end of file. When new bytes are written to the file, they are always appended to the end, even if the position is moved with the ostream:seekp function. 查询文件尾方式 ios:ate: The function performs a seek to the end of file. When the first new byte is written to the file, it is appended to the end, but when subsequent bytes are written, they are written to the current position., 输入方式 ios:in: The file is opened for input. The original file(if it exists) will not be truncated. 输出方式 ios:out: The file is opened for output. 更新方式 ios:trunc: If the file already exists, its contents are discarded. This mode is implied if ios:out is specifie

温馨提示

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

评论

0/150

提交评论