C/C++程序设计
1
异常 (exception)是程序控制中的偶发事件。异常的来源分两种:一种是硬件异常如 CPU触发的异常,一种是软件异常。软件异常是程序设计不周操作次序不当引发的意外。
异常处理就是关于不期望的事件发生后进行妥当应付的方法。 C++异常处理机制提供了捕获各种数据类型不测消息的能力。
C/C++程序设计
2
一、异常处理的概况二,C++异常处理的途径三、异常的多路捕获
C/C++程序设计
3
一、异常处理的概况程序设计中首先是确保代码正常运行,尽量避免错误。
因此在异常处理之前设置一套防范措施是更重要的编程工作。 C++异常处理并不着眼于错误的事先禁止,也无力应付硬件的莫测操作导致的软件的崩溃。
异常处理的共同焦点是错误发生后如何应对,有两种主要的风格:一种风格是在错误发生的现场进行清理工作,资源的分配和释放在同一个程序块中进行;另一种作风是在低层检查到的错误反馈到高层统一处理,低层对应被调函数的程序段,高层对应主控函数。总的原则是错误及时的捕获,
尽量有效地进行错误的排除,程序的损失减至最少。
C/C++程序设计
4
C++异常处理仅是各种异常处理方法中的一种而已。好的容错性强的程序设计是通过简单的 if~else语句、层次分明的短路逻辑判断表达式和方向直接的 goto语句一起组合实现的。
短路逻辑表达式负责轻重缓急的消息过滤,if~else语句掌管消息的分流,将正常情况的代码置于 if 分支下,异常的状况置于 else 分支下。 Goto 语句擅长将多层嵌套的内层犯规直接弹出到一个统一的外层出口,return带表达式直接返回更是被调函数运作成功与否的鲜明标志,程序员可以指定返回数值的实际语义。
C/C++程序设计
5
C++异常处理相对于早已有之的容错处理进行了如何的提炼和拔高呢?
首先该机制平和地过渡了被调函数和主控函数之间的界限,错误可以在低层及时清理也可以到高层收拾,这样低层的错误信息可以越过函数调用的分界向上引渡到主控函数,
直接主控函数不处理,间接的主控函数则予以过问,或最终输送到 main函数。
其次引进一组相关的语句专门处理异常,这样程序的分工就显得井然有序。
这一组专门实施异常处理的语句是关于结构化异常处理,if~else、堆栈动态跟踪等复杂技术的综合和抽象。
C/C++程序设计
6
二,C++异常处理的途径
C++提供了一个平行于或超然于函数调用链的异常处理上下环境,这个异常处理环境目前尚不够应对低层的硬件偶发错误,因此 C++异常处理适宜于高级程序员恰当抚平程序逻辑中暗伏的漏洞。
关键字 try,catch和 throw就是对异常进行处理的。该异常处理的优点在于对不测错误的拦截可在程序的任何地方做出响应,可以集中排放错误处理代码也可以随处布点跟踪同时尚可以一层一层地向上抛出。
C/C++程序设计
7
下面先介绍三个关键字的含义:
1,try { 内嵌 throw语句的语句序列; }
2,throw type_expre; throw;
3,catch ( type [expre] )
{匹配 throw 抛出的 type_expre 的语句块 ;}
catch (...) { 匹配任意类型的语句块 ; }
关键字 try紧跟一对花括号包含的语句块,称为 try块。
try块中的序列语句包含直接的或间接的 throw语句,这些 throw语句潜在的描向一个同级的 catch的入口,因此触发 catch块起作用。其中 type表示已经声明的类型,如
float,long以及结构名等。
C/C++程序设计
8
关键字 throw 跟随一个表达式语句,其后的表达式等价于 return所要求的操作数,其作用也类似于 return的返回转向作用。可以说带表达式的 throw语句是 return语句和 goto
标号语句的融合。 return将控制权返回给主控函数,goto将流程直接转移到指定的标号处但跳转由内向外地限于一个函数体内; throw可以远程抛射,流程从抛设点暗携 return的控制撤离机制,直奔所在 try块后的 catch块的单参入口。
throw也可近处投掷异常,相当于 goto的段内向下跳转 。
throw关键字不带表达式时表示类型信息流的默认传递,相当于在当前 catch块暗中接受了前次投掷过来的类型信息然后接着抛出,传递到后面的 catch块处理。
C/C++程序设计
9
关键字 catch之后是圆括号包括的单参数入口形参,其后再紧跟一对花括号定界的复合语句,形式上简直活脱脱是整个地将函数的定义体直接插入另一个函数代码中,大抵上也不妨这么看待,因此 catch块称为捕获处理器。定界的一对花括号和单参数入口的类名是不可或缺的,其后的形参
expre则可有可无。但一旦派上用途其 expre的作用域限于该 catch处理器。省掉 expre的形式,catch中不能接受
throw抛出的数值而只捕获类型信息。
省略号 (...)的入口捕获处理器表示处理任意类型的
throw表达式,可称为默认捕获处理器。因此这个默认捕获处理器放置在多个 catch块的最后,保证先执行特定类型的错误过滤,然后才进行一般的处理。
C/C++程序设计
10
throw抛出的表达式既有数值也有该数值对应的类型,
类型是第一重要的,其次才是数值。 throw抛出一个类型,
catch捕获处理器进行拦截,拦截的指标就是其入口的类型。如果捕获入口的类型和 throw投射的类型一致,则触发离抛射位置最近的 catch处理器,其次 throw表达式的值初始化 catch的形参;不带表达式的 throw默认投掷,其暗中接受的类型发射到下一个默认的 catch(...)处理器。
try块中一般包含至少一条 throw语句,根本不含 throw
语句的 try块形同虚设,导致其后的 catch成为死码区。不在
try块之内的 throw语句触发程序的非正常结束,是编程的禁忌。
C/C++程序设计
11
必须至少有一个 catch块紧随 try块之后,两者之间不能插入其它的语句。一个 try块可以带多个 catch块,多个
catch处理器的入口形参其类名在重载的含义上必须是无歧义的。
异常处理的语法 (其中 type1,type2 typen是已声明的类型如 int或 char*等 ) 格式为:
try { 内嵌若干 throw 语句的语句序列; }
catch (type1 [e1]) { 语句块 1; }
catch (type2 [e2]) { 语句块 2; }
catch (typen [en]) { 语句块 n; }
catch (...) {匹配任意类型的语句块 ;}
随后语句 ;
C/C++程序设计
12
如上的异常处理可称为 try~catch控制结构,这个控制结构就像 if~else控制结构是正常流程的一部分。 catch块中方括号括起来的表达式 e1,e2,en表示可选项。
流程通过正常次序进入 try块,有选择地触发一个 catch
块的运行。
流程的路由机制是,首先检查触发 throw语句所在的函数,确定 throw隶属的 try块,如果这一步成功,则根据
throw之后的表达式推演出其类型,用这个类型与 try块管辖的 catch块根据其先后出现的次序比较,如果探查到与对口的 catch块类型相吻合,则流程控制交给这个 catch块。省略号形式的 catch捕获处理器放置在最后,以免屏蔽其后的
catch块。
C/C++程序设计
13
如果一个异常得到成功的捕获处理且程序没有终止,则执行 try~catch控制结构之后的语句。
同样如果 try块的语句全部遍历并无 throw语句唤醒,流程无视所有的 catch块,
直接跳到 try~catch控制结构的随后语句继续执行。
如果触动了 throw语句抛出一个类型信息流,而没有相应的 catch捕获器予以化解,则企图启动 terminate函数,该函数调用 abort函数,程序非正常地退出,这种退出可引发运行错误。
C/C++程序设计
14
下面的例子一个是异常处理另一个是简单的 if语句加静态变量的跟踪,两者实现相同的功能。
程序员的自行跟踪技术代码略微复杂,但执行文件短小精悍,其 debug和 release版本大小分别为 153kb,28kb 。异常处理的界面统一但执行文件的负担增添许多,其 debug和
release版本大小分别为 161kb,32kb。
编译器在幕后进行了复杂的跟踪、堆栈消息过滤等通用的异常处理,因此代价是不可避免的。
资料显示异常机制导致的时间代价为 5%,空间大小为
%7左右。
C/C++程序设计
15
[例 ] C++异常处理技术
# include <stdio.h>
long DivThrow (long x,long y)
{ if (y==0) throw x;
return x/y;
}
void ExceptHanding (long u,long v)
{ try
{ long d=DivThrow (u,v);
printf ("ExceptHanding %d/%d=%d\n",u,v,d);
}
C/C++程序设计
16
catch (long x)
{printf ("ExceptHanding=%d,Can't divided by zero\n",x);
}
}
void main()
{ ExceptHanding(5,3);
ExceptHanding(5,0);
}
C/C++程序设计
17
[例 ] if语句加静态变量的跟踪
# include <stdio.h>
enum
{ NoZero=1,IsZero=1000 };
static int sTrace=NoZero;
long DivideIf (long x,long y)
{ if (y==0)
{ sTrace=IsZero; return x; }
return x/y;
}
C/C++程序设计
18
void TraceHanding (long u,long v)
{ long d=DivideIf (u,v);
switch (sTrace)
{ case NoZero,printf ("TraceHanding
%d/%d=%d\n",u,v,d);break;
case IsZero,printf ("TraceHanding %d,
Can't divided by zero\n",d); break; }
}
void main()
{ TraceHanding (6,2);
TraceHanding (2,0);
}
C/C++程序设计
19
C++异常处理并不具备异常发生后,程序的自恢复功能。
如何实施亡羊补牢的事后补救,程序员依然得小心设置静态的或外部的全局变量。
在可能冒出不测的程序段之前,保存现场到硬盘,然后通过异常处理的强大功能在合适的地方放置 catch捕获器,
对于最容易萌生的错误如求逆失败等安排相应的 catch块在路由的前面,然后及时的予以处理。
C/C++程序设计
20
三、异常的多路捕获
C++异常处理的优点在于可以捕获各种类型信息的异常,特别是面向对象的异常。
throw语句可以潜入比所隶属的 try~catch函数更内层的被调函数,但应保证 throw语句密切匹配同层的 catch块。
C/C++程序设计
21
[例 ] 异常的多路捕获
# include <stdio.h>
enum enumType
{ eChars,eLong,eClass,eUnknown,eSkip };
class ClassE{};
struct Unknown {};
void PolyHanding (int kind)
{ if (kind==eSkip) throw eSkip;
try { if (kind==eChars) throw "string type";
if (kind==eLong) throw (long) kind;
if (kind==eClass) throw ClassE ();
if (kind==eUnknown) throw Unknown(); }
C/C++程序设计
22
catch (char* s)
{ printf ("Except Handler is=%s\t",s); }
catch (long)
{ printf ("Except Handler is long type\t"); }
catch (ClassE)
{ printf ("Except Handler is ClassE type\t"); }
catch(...)
{ printf ("Except Handler is Unknown type\t"); }
printf ("Embeded try~catch block is not skip\n");
}
C/C++程序设计
23
void main()
{ try { PolyHanding (eChars);
PolyHanding (eLong);
PolyHanding (eClass);
PolyHanding (eUnknown);
PolyHanding (eSkip);
PolyHanding (eLong);
PolyHanding (eClass); }
catch (enumType)
{ printf ("Embeded try~catch block is skipped\n");}
printf ("this printf is a must route\n");
}
C/C++程序设计
24
Except Handler is=string type
Embeded try~catch block is not skipped
Except Handler is long type
Embeded try~catch block is not skipped
Except Handler is ClassE type
Embeded try~catch block is not skipped
Except Handler is Unknown type
Embeded try~catch block is not skipped
Embeded try~catch block is skipped
this printf is a must route
C/C++程序设计
25