H面向对象(异常处理)YH.ppt

上传人:本田雅阁 文档编号:3481424 上传时间:2019-09-01 格式:PPT 页数:52 大小:842.02KB
返回 下载 相关 举报
H面向对象(异常处理)YH.ppt_第1页
第1页 / 共52页
H面向对象(异常处理)YH.ppt_第2页
第2页 / 共52页
H面向对象(异常处理)YH.ppt_第3页
第3页 / 共52页
H面向对象(异常处理)YH.ppt_第4页
第4页 / 共52页
H面向对象(异常处理)YH.ppt_第5页
第5页 / 共52页
点击查看更多>>
资源描述

《H面向对象(异常处理)YH.ppt》由会员分享,可在线阅读,更多相关《H面向对象(异常处理)YH.ppt(52页珍藏版)》请在三一文库上搜索。

1、1-1,知识回顾,I/O流的概念 输出流 输入流,2,异常处理,第八章,1-3,理解异常处理的概念 掌握异常处理的实现 掌握异常处理中对象的构造与析构 理解名字空间的概述,本章目标,异常处理是C+语言中重要的错误处理机制,是提高程序容错性的一种手段。异常处理主要针对程序运行时出现的各种异常情况,提供发现,捕获异常的手段,并尽量减少异常对程序运行的影响。,有的程序虽然经过编译、连接成为可以运行的程序,但在运行过程中难免会出现各种各样的问题,即使对那些所谓能“正常运行”的程序而言也是如此。,程序中潜在的异常问题,提出问题,示例: #include #include using namespace

2、std; int main() float a,b,c; double x1,x2; couta; coutb; coutc;,x1=(-b+sqrt(b*b-4*a*c)/(2*a); x2=(-b-sqrt(b*b-4*a*c)/(2*a); cout“方程的实根是:x1=“x1endl; cout“方程的实根是:x2=“x2endl; return 0; ,从上面例子可以看出:能够“正常运行”的程序可能存在着许多潜在的“隐患”。程序运行可以检测到的一些非正常情况称为异常(exception)。 如除数为0、数组越界访问、内存空间不够、输入/输出不正常(文件找不到、输入数据类型错等)等。

3、异常是程序错误一种形式。,分析问题,程序中的错误按性质可分为语法错误、逻辑错误和异常3种。 一般来说,异常的检测和处理要完成下列任务之一: (1)让“用户”知道程序出现了异常,并退出程序。 (2)让“用户”知道程序出现了异常,允许“用户”选择继续使用程序。 (3)在程序发生异常时,能够在不打扰“用户”的情况下继续程序的运行。,C+语言异常处理机制的基本思想是将异常的检测与处理分离。当在一个函数体中检测到异常条件存在,但无法确定相应的处理方法时,将引发一个异常,并由函数直接或间接调用检测并处理这个异常。 这一基本思想用3个保留字实现:throw、try和catch。在一般情况下,被调用函数直接检

4、测到异常条件的存在并使用throw引发一个异常(注意,C+语言的异常是由程序员控制引发的,而不是由计算机硬件或程序运行环境控制的);在上层调用函数中使用try检测函数调用是否引发异常,检测到的各种异常由catch捕获并作相应处理。,异常处理实现,在VC+6.0环境中,为了使用异常处理机制,需要进行如下设置(默认设置): (1)选择菜单中的project。 (2)在弹出的下拉菜单中选择Setting命令,出现Settings对话框。 (3)打开C/C+选项卡。 (4)在Category中选择 C+ Language。 (5)选中Enable exception handling复选框。,在C+程

5、序中,任何需要检测异常的语句(包括函数调用)都必须在try语句块中执行,异常必须由紧跟着try语句后面的catch语句来捕获并处理。因而,try与catch总是结合使用。,1、异常处理的语法,throw、 try和catch语句的一般语法如下: throw 表达式; try /try语句块 catch(类型1 参数1) /针对类型1的异常处理 catch(类型2 参数2) /针对类型1的异常处理 . catch(类型n 参数n) /针对类型1的异常处理 ,异常处理的执行过程如下: (1)控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。 (2)如果在保护段执行期间没有引起异常,

6、那么跟在try块后的catch子句就不执行,程序从异常被抛掷的try块后跟随的最后一个catch子句后面的语句继续执行下去。 (3)如果在保护段执行期间或在保护段调用的任何函数中(直接或间接的调用)有异常被抛掷,则从通过throw运算数创建的对象中创建一个异常对象(可能包含一个复制构造函数)。 (4)如果匹配的处理器未找到,则运行函数terminate将被自动调用,而函数terminate的默认功能是调用abort终止程序。 (5)如果找到了一个匹配的catch处理程序,且它通过值进行捕获,则其形参通过复制异常对象进行初始化。,示例1: #include void main() char *b

7、uf; try buf=new char512; if(buf=0) throw “内存分配错误!“; cout“内存分配成功!“endl; catch(char *str) cout“异常引发:“strendl; ,示例2: #include int fun(int); void main() try cout“4!=“fun(4)endl; cout“-2!=“fun(-2)endl; cout“5!=“fun(5); catch (int n) cout“n=“n“不能计算n!“endl; cout“程序执行结束.“endl; ,int fun(int n) if(n=0) throw

8、n; int s=1; for(int i=1;i=n;i+) s*=i; return s; catch处理程序的出现次序很重要,因为在一个try块中,异常处理程序是按照它出现的次序检查的。只要找到一个匹配的异常类型,后面的异常处理都将被忽略。,示例3: #include void fun(int code) try if(code=0) throw code; /引发int类型的异常 if(code=1) throw x; /引发char类型的异常 if(code=2) throw 12.345; /引发double类型的异常 catch(int n) cout“捕获整数类型:“nendl

9、; catch(char c) cout“捕获字符类型:“cendl; ,catch(double d) cout“捕获双精度类型:“dendl; return; void main() fun(0); fun(1); fun(2); ,程序中同时列出多个catch语句时,将以catch语句在程序中出现的次序作类型匹配,并且只有一个匹配的catch语句被执行,其他的catch语句将被忽略。 catch(.)是一个特殊的捕获语句,可以捕获任何异常,因而在任何情况下其他catch子句都不被检查。所以,catch(.)应该放在最后。在VC+6.0中,若catch(.)不是放在所有catch(.)语句

10、的最后,则会出现编译错误。,2、捕获异常,示例1: #include void fun(int code) try if(code=0) throw code; /引发int类型的异常 if(code=1) throw x; /引发char类型的异常 if(code=2) throw 12.345; /引发double类型的异常 catch(int n) cout“捕获整数类型.“nendl; catch(.) cout“默认捕获.“endl; return; ,void main() fun(0); fun(1); fun(2); ,从前面异常处理的例子可以看出,调用一个函数时,除了了解函数

11、的参数与返回值外,还必须了解函数的异常引发方式,以便设计异常处理程序,应付函数调用过程中引发的异常。 异常的引发与捕获已成为函数之间界面的一部分,有必要在函数原型中也列出异常引发。 例如: void fun(int i) throw(t1,t2,t3);,3、带有异常声明的函数原型,C+的异常处理机制不仅能够处理各种不同类型的异常,还具有为抛出异常前构造所有局部对象自动调用析构函数的能力。 在程序中,找到一个匹配的catch异常处理后,如果catch子句的异常类型说明是一个值参数,则其初始化方式是复制被抛出的异常对象。如果catch子句的异常类型说明是一个引用,则其初始化方式是使该引用指向异常

12、对象。 当catch子句的异常类型说明参数被初始化后,便于始展开栈的过程。这包括将从对应的try块开始到异常被抛出之间构造(且尚未析构)的所有自动对象进行析构。析构的次序与构造的次序相反。然后程序从最后一个catch处理之后开始恢复执行。,异常处理中对象的构造与析构,示例: #include void fun(void); class A public: A() ; A() ; const char *ShowReason() const /异常处理成员函数 return “异常在A类中“; ;,class B public: B(); B(); ; B:B() cout“B构造函数“endl

13、; B:B() cout“B析构函数“endl; ,void fun() B b; cout“fun():抛掷一个A异常“endl; throw A(); ,void main() cout“进入main()“endl; try cout“在try块中调用fun()“endl; fun(); catch( A E) cout“在catch处理器捕获一个异常类型:“; coutE.ShowReason()endl; catch(char *str) cout“捕获其他异常:“strendl; cout“返回main()“endl; ,在名字空间中可以放入这样的声明:类、变量(以及它们的初始化)、

14、函数(以及它们的定义)、模板以及其他名字空间。从而这些变量或函数都与该名字空间相关联。,名字空间概述,1、名字空间的定义 保留字(namespace)用于定义名字空间。名字空间必须在程序的全局作用域内定义,不能在函数内或类内部定义,最外层名字空间的名字必须在程序的全局作用域惟一。 名字空间可以分多次定义,即可以先在初始定义中定义一部分成员,然后在扩展定义中再定义另一部分成员,或者再定义初始时声明函数原型。初始定义和扩展定义的语法格式相同。 保留字using用于声明程序要引入的名字空间成员,或都用于指示程序要引用的名字空间。在声明引用名字空间的某个成员之前,成员必须已经在名字空间中进行了声明或进

15、行了定义。,示例 #include namespace NS1 /初始定义名字空间NS1 extern int x; /说明整型变量x void fun(int); /说明函数fun(int) void fun(long) /定义函数fun(long) cout“Processing a long argument “endl; ,namespace NS1 /扩展定义名字空间NS1 int x=5; /定义整形变量x void fun(int) /定义函数fun(int) cout“Processing a int argument“endl; ,void main() int y=20;

16、using NS1:x; /说明引用变量x using:NS1:fun; /说明引用函数fun() x=10; fun(4); fun(4L); cout“x=“xendl; cout“y=“yendl; ,1、访问名字空间的成员 访问名字空间的成员有4种方式: (1)直接访问成员 格式如下: 名字空间名字:成员名字 因此,直接访问总能惟一地访问指定名字空间的成员。,(2)指定名字空间(使用using namespace 语句) 指定名字空间的格式如下: using namespace 名字空间; /直接使用成员名字 (3)声明引用成员(使用using语句) 声明引用成员的格式如下: usin

17、g 名字空间:名字 /直接使用成员名字,(4)使用别名法 声明引用成员的格式如下: namespace 别名=名字空间; /使用“别名:成员名字” 本方法与直接方法成员方法类似,只是加了一个别名。例如有以下两个名字空间NSA和NSB,分别声明了同名的类模板: namespace NSA template class Array private: T *ia; int ssize; ; ,以上的类Array被封装在名字空间NSA中,在使该类前,必须使NSA名字空间可见。 namespace NSB template class Array private: T *ia; int ssize; ;

18、 ,以上的类Array被封装在名字空间NSB中,在使该类前,必须使NSB名字空间可见。因为使用了名字空间,两个Array类分别在不同的名字空间中,所以不会存在冲突。其中四种使用方式如下: 第一种用法:直接用法。 NSA:Array a; NSB:Array b; 第二种用法:指定名字空间。 using namespace NSA; Array a; using namespace NSB; Array b;,第三种用法:声明引用成员。 using NSA:Array; Array a; using NSB:Array; /错误,存在同名的成员 Array b; 值得注意的是,上述语句是错误的,

19、应将两个类模板改为不同的名字,如将NSB中的Array改为Array1,则以下语句是正确的: using NSA:Array; Array a; using NSB:Array1; Array b;,第四种用法:使用别名法。 namespace us=NSA; namespace ms=NSB; us:Array a; ms:Array b;,示例: #include namespace NS1 int x=10; namespace NS2 int x=20; void main() using NS1:x; cout“x=“xendl; using NS2:x; cout“x=“xendl

20、; ,2、使用作用域运算符“:”访问成员 当名字空间的成员和程序的全局标识符同名时,可以通过作用域运算符“:”既定程序的全局标识符;当名字空间的成员和程序的标识符同名时,首先访问的是程序的局部标识符。,示例: #include int x=20; /全局变量 namespace NS1 int x=10; void main() using namespace NS1; cout“x=“:xendl; ,3、名字空间的嵌套 名字空间也可以像类那样嵌套,形成多个层次的作用域,因此,在访问名字空间的成员时,就有可能使用多个域运算符。,示例: #include namespace NS1 /NS1的

21、初始定义 int x=10; void fun1() cout“NS1s fun1()“endl; namespace NS2 int y=20; void fun2() cout“NS2s fun2()“endl; ,using NS1:fun1; /using说明,全局名字空间限定fun1 using NS1:x; /using说明,全局名字空间限定x using NS1:NS2:fun2; /using说明,多重名字空间限定fun2 using NS1:NS2:y; /using说明,多重名字空间限定y void main() fun1(); fun2(); cout“x=“xendl;

22、 cout“y=“yendl; ,4、 std名字空间 本章前面的程序都是使用标准C+编写的,其头文件都带有.h扩展名,而ANSI/ISO标准C+头文件不带扩展名。这是因为C+是从C发展而来,某些头文件,如math.h都是从C中沿袭过来的,而诸如iostream.h、iostreamip.h和fstream.h等是特别为C+设计的。当一个头文件被包含进某一个程序中,头文件中的全局标识符也就变成了程序中的全局标识符。在ANSI/ISO标准c+中,为了利用名字空间机制的先进之处,所有的头文件都被修改过,这样的标识符都被声明在std名字空间中。Std是C+标准库的名字空间,C+标准库中的所有定义都被

23、定义在这个名字空间中。,1、直接指定标识符 在所有特定的标识符剪辑上“std:”前缀。 #include /使用C+标准库 void main() int n=100; std:coutstd:hexnstd:endl; ,2、使用using关键字 对于每个特定的标识符,都用“using std: 标识符”进行声明。 #include /使用C+标准库 using std:cout; using std:endl; using std:hex; void main() int n=100; couthexnendl; ,3、使用using namespace std 这是一种最方便的方法,从而使std命名空间的内定义的所有标识符都有效,就好像它们被声明为全局变量一样。 #include /使用C+标准库 using namespace std; void main() int n=100; couthexnendl; ,异常和异常处理的概念 异常处理的实现 异常处理中对象的构造与析构 名字空间的概述,本章总结,

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

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1