ste_will

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

C++异常处理详解

C++的异常情况无非两种,一种是语法错误,即程序出现了错误的语句、函数结构和类,致使编译无法通过。另一种是运行时发生的错误,一般与算法有关。

C++异常处理机制:

C++异常处理机制是一个用来有效地处理运行错误的非常强大且灵活的工具,它提供了更多的弹性、安全性和稳固性,克服了传统方法所带来的问题. 异常的抛出和处理主要使用了以下三个关键字: try、 throw 、 catch 。抛出异常即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:
                                  throw   表达式;

如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获 并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。由于C++使用数据类型来区分不同的异常,因此在判断异常 时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。

异常的接口声明
为了加强程序的可读性,使函数的用户能够方便地知道所使用的函数会抛出哪些异常,可以在函数的声明中列出这个函数可能抛出的所有异常类型,例如:void   fun()    throw( A,B,C,D);

这表明函数fun()可能并且只可能抛出类型(A,B,C,D)及其子类型的异常。如果在函数的声明中没有包括异常的接口声明,则此函数可以抛出任何类型的异常,例如:void     fun();

一个不会抛出任何类型异常的函数可以进行如下形式的声明:void fun() thow();

     
异常处理中需要注意的问题
(1). 如果抛出的异常一直没有函数捕获(catch),则会一直上传到c++运行系统那里,导致 整个程序的终止。
(2). 一般在异常抛出后资源可以正常被释放,但注意如果在类的构造函数中抛出异常,系统是不会调用它的析构函数的,处理方法是:如果在构造函数中要抛出异常,则在抛出前要记得删除申请的资源。
(3). 异常处理仅仅通过类型而不是通过值来匹配的,所以catch块的参数可以没有参数名称,只需要参数类型。
(4). 函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突。
(5). 应该在throw语句后写上异常对象时,throw先通过Copy构造函数构造一个新对象,再把该新对象传递给 catch. 那么当异常抛出后新对象如何释放?
异常处理机制保证:异常抛出的新对象并非创建在函数栈上,而是创建在专用的异 常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。所有从try到throw语句之间构造起来的对象的析构函数将被自动调用。但如果一直上溯到main函数后还没有找到匹配的catch块,那么系统调用terminate()终止整个程序,这种情况下不能保证所有局部对象会被正确地销毁。
(6). catch块的参数推荐采用地址传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常扑获要放到父类异常扑获的前面,否则,派生类的异常无法被扑获。
(7). 编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊。

 

C++的异常对象时局部变量却不会在栈回退时被析构

C++的函数抛出异常后,该函数定义的所有局部变量(即定义在栈上的变量)都会被自动析构,后定义的先析构。但异常对象作为一个局部变量,却能在高层函数中保持有效性,这是一个很奇怪的现象。

实际上,在最初抛出异常的那个最深的函数中,抛出异常时,程序会先将异常对象用拷贝构造函数复制一份到“安全的”地方,然后就可以 肆无忌惮地将抛出异常函数中的所有局部变量(包括那个原始异常对象)通通析构。此后栈层层回退时,依然保留的是那个异常对象的副本。不过,那个副本在当前线 程离开异常控制流程(也就是不再处于任何一层catch block中)后就会被自动析构。

另外注意各个对象的地址。我们知道,函数调用时每一层函数调用都有一“帧”内存,所有局部变量都在那里面,而且函数调用得越深,其地址就会越小(如上面的输出所示,D1和D2的地址比C小)。而那个拷贝构造出的新·异常对象,居然比D1、D2的地址还要小。
这说明,在FuncD函数的内存帧的上面,被临时新增了一个帧(从新异常对象和D2的地址差可以看出,这个帧的位置是在异常产生时被临时生成的,而不是一开 始就预设好的)。这个帧在线程离开异常处理流程后会被析构(而在那之前,FuncD的帧早早就被清空了)。至于帧的大小暂时不清楚,不过应该用不着多大 ——只要能放下所针对的那个异常对象就行了。

posted on 2012-10-24 17:58  ste_will  阅读(262)  评论(0)    收藏  举报