260 likes | 389 Views
第 7 章 异常处理与 I/O 流. 王建兴 闽江学院物理学与电子信息工程系. 7.1 异常处理. 异常处理过程,就是预防运行时错误的发生,即在错误即将发生前通过检测触发它的条件来阻止它 。 C 语言没有内置的运行时错误处理机制,一般采用下列的方法来处理可能的运行时错误: ① 函数返回统一定义的状态编码来表操作成功、失败或其他错误信息。 ② 使用全局变量来保存错误编码,每一个使用到它的函数在开始是时候都检查它的值,并且每一个函数的结尾把状态信息写回到这个全局变量中。 ③ 出错时终止程序运行。. 7.1.1 异常处理的语法结构.
E N D
第7章 异常处理与I/O流 王建兴 闽江学院物理学与电子信息工程系
7.1 异常处理 • 异常处理过程,就是预防运行时错误的发生,即在错误即将发生前通过检测触发它的条件来阻止它 。 • C语言没有内置的运行时错误处理机制,一般采用下列的方法来处理可能的运行时错误: • ①函数返回统一定义的状态编码来表操作成功、失败或其他错误信息。 • ②使用全局变量来保存错误编码,每一个使用到它的函数在开始是时候都检查它的值,并且每一个函数的结尾把状态信息写回到这个全局变量中。 • ③出错时终止程序运行。
7.1.1 异常处理的语法结构 • 组成部分:抛出异常、提炼异常、捕获异常以及异常类型 • 抛出异常 throw 异常对象表达式; throw语句的异常对象类型就是异常类型
7.1.1 异常处理的语法结构 • 提炼异常和捕获异常 try { 复合语句} catch ( 异常类型[名称] ) { 复合语句} catch ( 异常类型[名称] ) { 复合语句} …… 【例7-1】
7.1.1 异常处理的语法结构 • 从程序结果中可以看出,在异常发生时,会自动调用在try块中从try到throw语句之间创建起来的局部对象的析构函数,从而正常的释放内存空间。如果在try块中局部对象是动态创建的,那么一般来说就需要在相应的catch块中释放内存空间,特别是catch块中有调用程序终止的语句时。 • 示例代码
7.1.2 异常的类型匹配规则 • C++规定:当一个异常对象和catch子句参数类型符合下列条件时,匹配成功: • l如果catch子句参数的类型就是异常对象的类型或其引用; • l如果catch子句参数类型是异常对象所属类型的public基类; • l如果catch子句参数类型为基类指针或引用,而异常对象为派生类指针或引用; • lcatch子句参数类型为void*,而异常对象为任何类型的指针; • lcatch子句为catch-all,即catch(…)。 • 示例
7.1.2 异常的类型匹配规则 • 在异常组合中,要合理安排异常处理的层次:一定要把派生异常捕获放在基类异常捕获的前面,否则派生类异常匹配永远也不会执行。如果实在无法判断到底会有什么异常抛出,那就使用catch(void *)和catch(…),但是必须放在异常组合的最后面。 • catch块的参数应当采用引用传递而不是值传递。原因有二:一是异常对象可能会在调用链中上溯好几个层次才能遇到匹配的处理块,此时引用传递比值传递的效率高得多;二是可以利用异常对象的多态性,因为异常类型可能是多态类。可以抛出一个异常对象的地址,那么catch块中的参数就是异常基类型的指针。
7.1.4 异常处理的其它方面 • 异常类型和异常对象 • 任何一种类型都可以当作异常类型 ,异常仅通过类型而不是通过对象的值来匹配的。 • 但是一般不使用基本数据类型的对象作为异常,因为它们表示异常的能力不足;相反,总是自定义一些异常类来具体描述我们需要的异常类型。 • 【例7-3】
7.1.4 异常处理的其它方面 • 异常说明 • 异常说明是为了加强程序的可读性 。 Double Devide(double x,double y) throw(DevidedByZero); //表示抛出一种异常 Bool F(const char *) throw(T1,T2,T3); //表示可能有三种异常 • ①能清楚地告诉函数的调用者,该函数可能会抛出什么异常,以便用户能够编写正确的的异常处理结构。 • ②用户一般无法看到函数的实现(例如库函数),因此用户只能浏览函数原型才知道可能会抛出哪些类型的异常。
7.1.4 异常处理的其它方面 • 异常重抛或转换 异常重抛(rethrow),是在catch块中使用一个空throw语句来达到此目的,重抛后程序立刻退出当前try/catch范围而进入上一层范围,有可能是上一层调用者,也有可能是嵌套的try/catch结构的上一级。 也可以在catch块内抛出一个不同于当前异常类型的异常对象,这样可以实现异常转换,并让上层调用者来进一步处理。
7.2 I/0流与文件—流的概念 • “流”取意于“水流”、“车流”中的流,表示一种位置的动态变化。 • 数据从任一位置“流”动到另一个位置都可表示为字节序列的“流”动。 • 根据“流”的位置和传输特征,可分为文件流、I/O流、内存流、网络流以及加解密流等概念。 • C++流库是C++为了完成输入/输出工作而定义的一套类的集合,这些类构成一个层次系统。
ios(I/O流) istream (输入流) ostream (输出流) iostream (输入/输出流) ifstream (文件输入流) fstream (文件输入/输出流) ofstream (文件输出流) 7.2 I/0流与文件—流的概念 • ios类(<ios.h>)及其类层次是最重要的内容。ios类定义了用户经常使用的输入输出控制函数和一些有关格式控制、状态检测的枚举常量。 • 它们的分别定义如下的头文件:<ios.h>、<istream.h>、<ostream.h>、<iostream.h>和<fstream.h>。
7.2 I/0流与文件—流的概念 • C++的流库预定义了4个流对象,它们是istream类对象cin(标准输入流,默认为键盘),ostream类对象cout(标准输出流)、cerr(标准错误流)、clog(标准日志流),这三个对象默认的设备都是显示终端。
7.2.2 C++中的文件输入/输出 • (1) C++文件流类 • 从ios类派生出文件输入流类ifstream,文件输出流类ofstream,文件输入/输出流fstream,分别用于文件的输入、输出操作。 • ofstream类:用来打开(创建)文件、并执行文件的输出操作。 • ifstream类:用来打开文件,并执行文件的输入操作。 • fstream类:它兼有ofstream和ifstream基本性质,可以打开同时用于输入和输出的文件。 • 【例7-5】 【例7-6】
7.2.2 C++中的文件输入/输出 • 由上面的两个例子,可得出看出对文件的操作过程如下: ①根据文件的实际输入输出情况定义ifstream、ofstream、fstream类的对象; ②调用对象的成员函数open()来打开文件或通过构造函数来直接打开文件; ③对文件进行操作; ④操作完毕后,用成员函数close()关闭文件。
7.2.2 C++中的文件输入/输出 • 对于三个文件流类,都有四个重载的构造函数,现阐述如下: 其中最常用是: ofstream(char * pFileName, int mode, int prot);
7.2.2 C++中的文件输入/输出 • 关于三个参数的解释如下: • ①pFileName:文件名指针,要打开(创建)的文件名保存在pFileName所指向的字符串中。 • ②mode:文件的打开方式 名称 描述 ios::in 打开一个可读取文件,如果文件不存在创建文件 ios::out 打开一个可写入文件,如果文件不存在创建文件 ios::app 写入的所有数据将被追加到文件的末尾,此方式使用ios::out ios::ate 写入的所有数据将被追加到文件的末尾,此方式不使用ios::out ios::trunk 打开一个文件,如果文件已存在,删除文件原来已存在的内容。 ios::nocreate 如果文件存在,则打开该文件,否则将返回一个错误。 ios::noreplace 如果文件不存在,创建并打开该文件,否则将返回一个错误。 ios::binary 以二进制的形式打开一个文件,缺省为文本文件。
7.2.2 C++中的文件输入/输出 • 关于三个参数的解释如下: • ③ prot:文件的读写权限 名称 描述 filebuf::openprot兼容共享方式 filebuf::sh_none独占,不允许共享 filebuf::sh_read允许读共享 filebuf::sh_write允许写共享 【例7-7】
7.2.2 C++中的文件输入/输出 • 函数seekg()将把定位到指定的位置。可以使用: • lios::beg:移动到文件首端; • lios::end:移动到文件末端; • l一个负整数,如-5:定位到当前位置的5个字符以前; • l一个正整数,如5:向后跳过5个字符; l带两个参数,如(-5,ios::end):读文件文本的最后4个字符,操作过程是到达了末尾(ios::end),接着到达了末尾的前五个字符的位置(-5)。
7.2.2 C++中的文件输入/输出 • (2) 文件的操作方式 • ①文件读写函数 • lget(char):从文件中读取一个字节; • lgetline(char *pch, int count, char delim=’\n’):从文件中读取 count个字节,delim为读取时的结束符; • lread(char *pch, int count):从文件中读取count个字节,常用于二进制文件。 • lput(char ch):向文件写入一个字节; • lwrite(const char *pch, int count):向文件写入count个字节,常用于二进制文件。
7.2.2 C++中的文件输入/输出 • (2) 文件的操作方式 • 文件检测函数 • lgood():无错误发生,返回非零; • lbad():流操作发生错误,返回非零; • lfail():流操作发生错误,返回非零。 • leof():数据流到达文件尾,返回真值非零;文件尾表示文件最一个字节的后面; • lis_open():检测一个文件流对象是否关联打开某个文件,即检测是否使用open函数和构造函数打开某个文件;返回非零为真;
7.2.2 C++中的文件输入/输出 • (3)二进制的操作方式 • get()函数与put()函数具有读/写二进制格式文件的能力:读取一个字节,使用get()函数;写入一个字节,则使用put ()函数。 • 如果要读/写一整块的数据,那么可以使用read()和write()函数。 • 【例7-8】【例7-9】
7.2.2 C++中的文件输入/输出 • (4)文件的随机读写 文件的随机读使用函数seekg(),随机写使用函数seekp(),函数名中的g和p分别表示get与put,它们的原型是: istream & seekg(streamoff offset,seek_dir origin); ostream & seekp(streamoff offset,seek_dir origin); • 文件读写指针的当前位置可用以下函数确定: streampos tellg(); //返回当前读指针的位置 streampos tellp();//返回当前写指针的位置 【例7-10】
7.2.3 格式化输入与输出 • 对输入流与输出流进行数据格式控制,有两种方式:一种使用成员函数;一种使用操纵符。 • (1)用ios类的成员函数实现格式化 • 设置状态标志 • 清除状态标志 • 取状态标志 【例7-12】
7.2.3 格式化输入与输出 • ios类中除了定义了操作状态标志的成员函数之外,还提供了能设置域宽、填充字符和显示精度的成员函数。 (1)设置域宽 (2)设置填充字符 (3)设置显示精度 【例7-13】
7.2.3 格式化输入与输出 • (2)用操纵符实现格式化 • 操纵符算子:表7-2 【例7-14】 • 自定义操纵符 【例7-15】