Chinaunix首页 | 论坛 | 博客
  • 博客访问: 291017
  • 博文数量: 148
  • 博客积分: 4365
  • 博客等级: 上校
  • 技术积分: 1566
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-05 21:38
文章分类
文章存档

2014年(2)

2013年(45)

2012年(18)

2011年(1)

2009年(54)

2008年(28)

我的朋友

分类: C/C++

2008-11-03 16:36:35

抛出异常时无非以下两种情形:
1.throw语句抛出异常时,如果其在一个try块内,则搜索该try块对应的catch语句
2.某函数的throw语句抛出异常,该函数在try块内被调用,搜索该try块对应的catch语句
如果找到相应的handler,进行处理;没有找到,终止当前函数,释放临时对象所占的内存,返回其上层,即其的调用者按以上两步骤寻找。

上面的过程成为堆栈展开(stack unwinding),直到找到对应的catch语句,完成处理,然后从该catch语句对应的try块的对应的最后一个catch语句后顺序往下执行。
此过程中,临时对象中有类对象,会自动调用其析构函数进行析构。
所以析构函数内不要抛出任何异常,如果有异常抛出,会转到 library function terminate,通常abort当前程序。析构函数正在运行,说明当前可能就是在一个异常的处理当中。
而在构造函数内却可以抛出异常,如用new申请内存失败等,此时该对象是不完整的,这样的对象必须被destroy。构造函数中遇到异常,那些以创建的类类型对象会被调用析构函数释放掉,所以内存资源的管理最好封装到内类实现。

catch语句捕获的异常必须是完整类型,可以是那些buildin类型,当是类类型时,在该catch语句前面必须有该类的完整实现,只有class XXX;这样的声明是不可以的。

寻找catch语句时,最先发现的能对该异常进行处理的catch语句会被使用,并且不再往下寻找,故,当有很多异常时,出现概率最大的异常对应的catch语句应写在众catch的最上面。
catch语句的匹配要比函数调用时参数的匹配严格的多,只有以下三种转换是被允许的
1.nonconst-const,throw出一个nonconst异常对象时,可以匹配catch const XX的,XX可以是对象,也可是引用。
2.派生类型-基类型的转换。
3.数组与函数转换为对应的指针。

catch语句的参数是对象还是引用的区别与函数参数相同。通常当要处理的异常是某类派生体系中的时,将catch参数定义为引用。这样catch参数为基类型引用时,能匹配异常为派生类对象。参数为基类型对象时,也能匹配派生类对象,派生类特有部分被 slice down。
因为如此,处理派生类异常的语句一定要在处理基类型异常catch语句的上方,否则,每次进入的都是基类型的异常处理。即经过最多派生次数产生的异常对象的处理在最上方,按顺序往下。

如果进入的catch语句不能很好的处理该异常,它还可以将该异常重新抛出给其它的catch。
重新抛出异常的语句为,该语句必须在一个catch或其调用的函数内

throw;

重新抛出的异常取决于该异常的动态类型,而不是该catch语句参数的静态类型,参数为基类型的catch完全可以重新抛出派生类型异常(需原始产生的即为派生类异常才可)。
如果重新抛出异常所在的catch语句参数为引用,那么对该异常修改后重新抛出,再接收到异常的catch语句收到的是修改后的版本

前面我们提到,如果异常对应的catch语句到最后也没有找到,程序通常在terminate的作用下退出,我们可以设计一个兜底的catch语句,来处理所有的异常,当然它要位于最后面。

catch(...){
}

这样的catch语句可以捕获所有异常,切记使用它,一定要写在最后面。

类的构造函数是可以抛出异常的,当冒号语法后面的构造初始化出现异常怎么办,构造函数体内的catch语句肯定处理不了它。方法是将构造函数写成try块的形式。这种应用不是很多见。

class base{
public:
    base(int );
    int i;
};
base::base(int a)
    try:i(a){
    //函数体

}
catch(//*相应异常*/

        )
{
    /*异常处理*/

    }

保证程序异常安全的重要手段是在构造函数中申请资源(使用new等),在析构函数中释放它,即用类来管理。

我们在定义函数时如果事先知道其能抛出什么异常将有助于我们的工作,通知编译器,也有助于代码的优化。

void func(int) throw (runtime_error e);

我们声明函数func将要抛出runtime_error类或其派生类的异常。
如果throw语句为空 throw(); 说明该函数什么异常也不抛出,如果像平时的函数一样没有throw语句,则可以抛出任何异常。
但是异常说明不同于返回值与参数说明那样严格,只是方便程序员与编译器,完全可以有下面的函数

void func(int) throw ()
{
    throw runtime_error("error");
}

只是辛苦了一下编译器,其实函数异常说明像返回值,参数一样是函数原型的一部分。

基类与派生类中的虚函数的异常说明可以不同,但派生类中虚函数的异常说明必须严格于基类的,以下规则
1.抛异常数量少严格于数量多。
2.数量相同时,派生类异常严格于基类异常,即派生类对应的虚函数不能比基类多抛异常。
3.throw(); 什么异常也不抛最严格。
函数指针的赋值也遵循上述规则。
阅读(632) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~