C++异常处理

一、定义:

    异常处理机制实际上是一种运行时的通知机制。当程序异常的时候,一般就直接异常退出了。然而,一个健壮的软件是指在软件异常坏境下仍然能够正常运行的能力,异常处理机制能够保证在我们程序异常的时候执行对应的异常处理程序,而不是暴力的终止程序的执行。

二、异常语法结构:

    异常机制由try和catch语句组成,格式如下:

    try
    {
      if(...)
      {
        throw 对象或者变量或者常量
      }
    }
    catch(类型或者类型变量)
    {
    }
    catch(类型或者类型变量)
    {
    }
    catch(...)
    {
      //有可能再次抛出异常
      
//throw;       //throw 变量/对象/常量     }

注意事项:

   (1)catch语句紧跟在try语句之后,中间不能有任何其它语句

   (2)throw抛出一个异常,可以接一个对象,或者变量,或者常量

   (3)catch捕捉一个异常,catch括号内可以填写throw抛出的异常对象或者变量或者常量的类型. 此时则不能

访问throw抛出的内容。

   (4)catch捕捉一个异常,catch括号内可以还可以填写throw抛出的异常对象,变量. 此时就可以访问throw抛出的内容。

    (5)catch(...)表是捕获所有的异常。

   (6)如果当前函数产生异常后,没有对异常进行处理,则上一层函数将捕获异常进行处理。倘若上一层也没有捕获异常,则再交给上上层处理,这样一直往上传递,直到最顶层。最顶层不对异常进行处理,程序就退出了。

       (7)catch语句中还可以再次使用“throw ” 抛出一个空异常 或者 "throw 变量或者对象或者常量" 再次抛出一个异常,交给上一层进行捕获处理。

具体来看一个例子

例子1      catch括号中定义throw抛出的异常变量

#include <iostream>
using namespace std;

//自定义异常类
class MyExcept
{
private:
    char  m_acReason[24];        //异常原因
public:
    MyExcept(char * pcReason)
    {
        memset(m_acReason,0x00,sizeof(m_acReason));
        memcpy(m_acReason,pcReason,strlen(pcReason));
    }
    char * OutPutReason()        //输出异常原因
    {
        return m_acReason;
    }
};

//根据nVal的取值范围产生不同的异常(int , char * , MyExcept)
int HappenExcept(int nVal)throw(int,char *,MyExcel)    //表示可能会产生这三种异常
{
    try
    {
        if(nVal < 0)                                      //整形异常
        {
            throw 0;        
        }
        else if(nVal < 100)                                //字符串异常
        {
            throw "the value small 100";
        }
        else if(nVal < 200)                                //对象异常
        {
            throw MyExcept("this value small 200");
        }
        else if(nVal < 300)                                //未定义异常
        {
            //因为300.12是一个float类型,catch语句中
            //并没有捕获float异常
            throw 300.12;
        }
        else                                              //正常处理流程
        {
            cout << "您的值大于300" << endl;
            
        }
    }
    catch(int  nRetVal)                                    //捕获整形异常
    {
        cout << "返回整数: " << nRetVal << endl;
    }
    catch(char * pcRetVal)                                //捕获字符串异常
    {
        cout << "返回字符串:" << pcRetVal << endl;
    }
    catch(MyExcept & ExceptObj)                            //捕获对象异常
    {
        cout << "返回异常对象:" << ExceptObj.OutPutReason() << endl;
    }    
    catch(...)                                            //捕获未定义异常
    {
        //捕获未定义异常后,再次抛出异常,让上一层处理
        //下面两种方式都可以再次抛出异常

        //throw ;
        throw MyExcept("值小于300");
    }

    return 0;
}

//测试异常函数
int TestExcept()throw()                     //表示不会产生异常                  
{
    try
    {
        HappenExcept(260);
    }
    catch(...)                                            //捕获异常,处理下一层产生的异常
    {
        cout << "已经检测到未知异常,程序退出" << endl;
    }
    
    return 0;
}

int main()
{
    
    TestExcept();
    
    return 0;
}

运行结果:

这个例子验证了上述注意事项的所有规则,catch括号中定义了相应异常类型变量,用来输出异常信息。

catch括号中也可以只定义异常类型,而不定义异常类型变量,此时不能够获取到异常的信息。

例子2     catch括号中只定义异常类型,而不定义异常类型变量

#include <iostream>
using namespace std;

//自定义异常类
class MyExcept
{
private:
    char  m_acReason[24];        //异常原因
public:
    MyExcept(char * pcReason)
    {
        memset(m_acReason,0x00,sizeof(m_acReason));
        memcpy(m_acReason,pcReason,strlen(pcReason));
    }
    char * OutPutReason()        //输出异常原因
    {
        return m_acReason;
    }
};

//根据nVal的取值范围产生不同的异常
int HappenExcept(int nVal)
{
    try
    {
        if(nVal < 0)                                //整形异常
        {
            throw 0;
        }
        else if(nVal < 100)                            //字符串异常
        {
            throw "the value small 100";
        }
        else if(nVal < 200)                            //对象异常
        {
            throw MyExcept("the value small 200");
        }
        else if(nVal < 300)                            //未定义异常
        {
            //因为300.12是一个float类型,catch语句中
            //并没有捕获float异常

            throw 300.12;
        }
        else                                        //正常执行流程
        {
            cout << "您的值大于300" << endl;
        }
        
    }                                                
    catch(int &)                                    //捕获整形异常
    {
        cout << "返回整形" << endl;
    }                                                
    catch(char *)                                    //捕获字符串异常
    {
        cout << "返回字符串" << endl;
    }                                                
    catch(MyExcept &)                                //捕获对象异常
    {
        cout << "返回自定义对象" << endl;
    }
    catch(...)                                        //捕获未定义异常
    {
        //捕获未定义异常后,再次抛出异常,让上一层处理
        throw;
    }
    return 0;
}

//测试异常函数
int TestExcept()
{
    try
    {
        HappenExcept(260);
    }
    catch(...)                                            //捕获异常,处理下一层产生的异常
    {
        cout << "已经检测到未知异常,程序退出" << endl;
    }
    
    return 0;
}

int main()
{
    
    TestExcept();
    
    return 0;
}

运行结果和上面是一样的。

三、异常堆栈

  虽然异常看上去像局部对象,但是它并非创建在函数堆栈上,而是创建在专用的异常堆栈上,因此它可以跨越多个函数而传递到上层,否则在堆栈清退的过程中就会被销毁。 不要企图把局部对象的地址作为异常对象抛出,因为局部对象会在异常抛出后函数堆栈清退过程中被销毁。

四、异常说明以及冲突

  在使用了c++异常处理机制的环境中,应当使用函数异常说明,有两个基本原因:

  (1)清楚告诉函数的调用者,该函数可能会抛出哪些类型的异常,以便用户能够编写合适的异常处理器

  (2)用户一般无法看到函数的实现(例如库函数),因此用户只能浏览函数原型才能知道一个函数可能会抛出哪些异常类型。

  举个例子:

      int HappenExcept(int nVal)throw(int , char *, MyExcept &);  //可能抛出这三种类型
      int TestExcept()throw();                      //不抛出异常
      int myFun();                             //可能抛出异常,也可能不抛出

   使用函数异常说明的好处: 不仅可以约束函数的实现者,防止抛出异常说明列表中没有说明的异常,而且可以指导函数的调用者编写正确的异常处理程序。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。