现在的位置: 首页 > 综合 > 正文

C++异常处理

2013年08月12日 ⁄ 综合 ⁄ 共 3090字 ⁄ 字号 评论关闭

   

      在C语言中是没有异常处理机制的,异常处理是C++中的一个对程序运行出错或逻辑出错的一个处理机制,因为C++处理的问题比C语言处理的问题更为复杂,所以不能像C那样,总是假定程序能正常运行,不会发生错误。在C中要想处理程序运行中出现的错误,也只能够用if语句去逐个检查和判断。不幸当发生问题时,程序直接崩溃,没有提供出错信息,让人摸不着头脑。C++中的异常处理使我们能够将问题的检测和问题的解决分离,这样程序的问题检测部分可以不必了解如何处理问题。异常处理中,需要由问题检测部分招聘一个对象给处理代码,通过这个对象的类型和内容,两个部分能够就出现了什么错误进行通信,并向处理外部提供出错信息给用户或在程序中进行错误处理。

    一、异常的抛出

    1、异常是通过抛出(throw)对象而引发的,例如,throw runtime_error("Date Error");。被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那个。不存在数组或函数类型的异常,如果抛出一个数组,被抛出的对象转换为指向数组首元素的指针,如果抛出一个函数,函数被转换为指向该函数的指针。

    

    2、执行throw的时候,不会执行跟在throw后面的语句,而是将控制从throw转移到匹配的catch,抛出的对象不是局部存储,而是用throw表达式初始化一个称为异常对象的特殊对象。抛出的异常通过栈展开,沿嵌套调用链继续向上,直到为异常找到一个catch子句。只要找到能够处理异常的catch子句,就进入该catch子句,并在该处理代码中继续执行。当听时候,在紧接在与该try相关的最后一个catch子句之后的点继续执行。栈展开期间撤消局部对象,释放局部对象所用的内在并运行类类型局部对象的析构函数。

 

    3、当抛出一个表达式的时候,其类型来自某个继承层次,被抛出对象的静态编译时类型将决定异常对象的类型。异常对象的类型都与指针的静态类型相匹配,如果该指针是一个指向派生类对象的基类类型指针,则那个对象将被分割,只抛出基类部分

 

    4、注意,如果一个块直接分配内存,而且在释放资源之前发生异常,在栈展开期间将不会释放该资源,例如用new动态分配的内存,在栈展开期间不会被释放。可用auto_ptr解决这个问题。

 

    5、析构函数应该从不抛出异常。如果找不到匹配的catch,程序就调用库函数terminate.

 

    二、异常的处理

    1、用catch子句捕获异常。catch子句中的异常说明符的类型决定了处理代码能够捕获的异常种类。类型必须是完全类型,即必须是内置类型或者是已经定义的程序员自定义类型。大多数的转换都是不允许的,除了const到非const、派生类类型到基类类型、数组和函数到指针的转换。

 

    2、匹配原则:找到的catch不必是与异常最匹配的那个catch,相反,将选中第一个找到的可以处理该异常的catch。基类的异常说明符可以用于捕获派生类型的异常对象,使用来自继承层次的异常的程序必须将它们的catch子句排序,以便派生类型的处理代码出现在其基类类型的catch之前。无论什么时候,可以捕获任何异常的catch(...)都应放在最后,否则其他的catch语句将永远无法捕获异常。

 

    3、构造函数与异常。例如:

template <class T> Handle<T>::Handle (T *p)

try:ptr(p),use(new size_t(1))

{

}

catch(const std::bad_alloc &e)

{

    handle_out_of_menory(e);

}

关键字try出现在成员初始化列表之前,并且测试块的复合语句包围了构造函数的函数体。catch子句可以处理从成员初始化列表中抛出的异常和函数体中抛出的异常。

 

    4、catch可以改变它的形参,但只有当异常说明符是引用的时候,才会传播那些改变

 

三、auto_ptr指针

    1、auto_ptr是一个类型形参的模板,它为动态分配的对象提供异常安全。定义在头文件menory中。如,auto_ptr<int> ip;

 

    2、auto_ptr只能用于管理从new返回的一个对象,不能将auto_ptr存储在标准库容器中,不能用于指向动态分配的数组。

 

    3、为异常安全的内在分配使用auto_ptr,编译器保证在展开栈越过对应作用域之前,运行析构函数,释放auto_ptr所指向的内存。

 

    4、auto_ptr是可以任何类型指针的模板。auto_ptr的主要目的是,在保证自动删除auto_ptr对象引用的对象的同时,支持普通指针行为。

 

    5、auto_ptr对象的复制和赋值是破坏性操作。

例如,auto_ptr<int> ap1(ap2)

   
创建名为ap1的auto_ptr对象,ap1保存原来存储在ap2中的指针。将所有权转给ap1,ap2成为未绑定的。

例如,ap1=ap2

   
首先删除ap1指向的对象,然后将ap1置为ap2所指的对象,最后,ap2是未绑定的auto_ptr对象。

 

    6、要测试auto_ptr对象,必须使用它的get成员,返回包含在auto_ptr对象中的基础指针。

        例如:if(p_auto.get())

必须调用reset函数来改变指针。

        例如:p_auto.reset(new int(1024))

必须使用初始化的直接形式来创建auto_ptr对象。

       
例如:auto_ptr<int> pi=new int(1024);//error

              auto_ptr<int> pi(new int(1024));//ok

 

四、异常说明

    1、异常说明指定,如果函数抛出异常,被抛出的异常将是包含在该说明中的一种,或者是从列出的异常中派生的类型。异常说明跟在函数形参表之后,是函数接口的一部分。空说明列表指出函数不抛出任何异常。

例如;void no_problem()throw();

 

    2、基类中虚函数的异常说明,可以与派生类中对应虚函数的异常说明不同。派生类虚函数的异常说明必须与对应基类虚函数的异常说明同样严格,或者比后者更受限。

例如:

class Base

{

    public:

        virtual double fa(double) throw();

        virtual int fb(int) throw(std::logic_error);

        virtual std::string fc()throw(std::logic_error,std::runtime_error);

};

 

class Derived:public Base

{

    public:

        double fa(double) throw(std::underflow_error);//error

        int fb(int) throw(std::logic_error);//ok

        std::string fc() throw();//ok

};

 

    3、源指针的异常说明必须至少与目标指针的一样严格。

例如:

void fun(int) throw(runtime_error);

void (*pf1)(int) throw(runtime_error)=fun;//ok

void (*pf2)(int) throw(runtime_error,logic_error)=fun;//ok

void (*pf3)(int) throw()=fun;//error

void (*fp4)(int0=fun;//ok;

 

抱歉!评论已关闭.