学科分类
目录
C++基础

异常处理结构

C++中通过try、throw、catch结构实现了异常的检测、抛出及捕捉。简单的异常处理过程如下所示:

try{                                        //异常检测
        …
        throw  异常类型1对应的异常值;        //抛出异常,这里抛出异常类型1的异常值
    }    
    catch(异常类型1)                           //捕捉异常类型1的异常
    {
进行异常处理的语句
    }
    catch(异常类型2)                           //捕捉异常类型2的异常
  {
    进行异常处理的语句
  }

上述结构中,try块检测异常,若出现异常则由throw抛出,通常throw后跟随抛出的异常值或异常对象,本段代码中抛出的是异常类型1的异常值。抛出异常后从try块对应的后续各catch子句中依次寻找与抛出异常类型匹配的结构,匹配成功时进入该catch子句完成异常处理。

通过上述异常处理描述,总结异常处理的基本流程如下:

1、对某段可能产生异常的代码或函数使用try结构进行检测。

2、如果在执行try结构期间没有引起异常,则跟在try后面的catch结构不会执行。程序从try结构后跟随的最后一个catch子句后面的语句继续向下执行。

3、如果在执行try结构期间发生异常,则在异常发生的位置使用throw抛出异常,一个异常对象将被创建。

4、抛出异常后,若异常的抛出点在一个try结构中,则该try语句后的catch子句会按顺序检查异常类型是否与声明的类型匹配;若异常抛出点不在任何try结构中,或抛出的异常与各个catch子句声明的类型皆不匹配,则结束当前函数的执行,回到当前函数的调用点,把调用点作为异常的抛出点,重复上述过程,直到异常被某catch子句捕获,执行catch子句内容完成异常处理,当前try-catch结构执行完毕。

5、若始终未找到与异常匹配的catch子句,即到main()函数还未实现异常匹配,则运行库函数terminate(),终止程序。

下面对C++中检测、抛出、捕捉异常的三种语法规则及使用方法进行详细介绍。

1、 检测异常try

try结构用于检测异常,具体的语法格式如下所示:

try{
    语句
}

关键字try后用花括号包含待检测的语句块,在try结构中应直接包含抛出异常的throw语句或调用包含抛出异常的函数,try结构中的内容如下所示:

try{
         …                                        //可能产生异常的代码
         throw  异常值;                          //抛出异常
         //function();                         //调用可能会抛出异常的函数                         
}
catch(type1)
{
        …                                        //对应类型的异常处理代码
}
catch(type2)
{
         …                                       //对应类型的异常处理代码
}

try块检测到异常后,会去最近进入的还未退出的try块寻找能与异常类型匹配的catch结构,若匹配不成功,则可以寻找更外层的try块对应的catch结构,直到异常类型匹配或变为未捕捉的异常。

需要注意的是,try块定义了一个语句块,在其中可以定义局部变量,在try块外部该局部变量不可用。

2、 抛出异常throw

抛出异常是通过throw语句实现的,该语句由throw及表达式构成,定义格式如下:

throw 表达式;

语法形式上,throw类似于return语句。throw后面的表达式可以是常数、变量或对象。这里表达式类型比表达式值更重要,因为在catch结构捕捉异常时,是针对抛出异常类型进行判断,如果类型一致,则catch捕捉该异常,否则再去后续的catch块中重新进行匹配查找。

在使用throw抛出异常时,可以给出如下代码进行异常抛出:

throw 1;                     //抛出int类型的异常,数值为1
throw ("异常");                 //抛出char *类型的异常,内容为“异常”

catch块中是通过异常值类型完成匹配,因此若想通过throw对抛出的多个异常分别进行处理,需要throw后加不同类型的表达式,而不能根据表达式的不同数值区分不同类型的异常,若有如下代码:

throw 1;                     //抛出值为1的int类型异常
throw 2;                     //抛出值为2的int类型异常

上述代码抛出的异常将被同一个catch块处理。

3、 捕获异常catch

抛出异常后,程序的控制权将被异常类型匹配成功的catch块获得,catch块用于处理异常,catch块也被称为异常处理器,其定义结构如下所示:

catch(异常类型1声明)
{
}
catch(异常类型2声明)
{
}
…
catch(异常类型n声明)
{
}

通常try块对应的catch块会有多个,用于处理不同类型的异常。catch后面括号中的异常类型声明可以是类型或对象声明,花括号中描述用于异常处理的代码。catch块的书写格式类似于函数定义,异常类型声明相当于函数参数。

catch块必须直接放在try块之后,根据catch的排列顺序依次对抛出的异常进行测试,若异常类型与某catch块的异常类型声明匹配则进入该catch块完成异常处理。

另外,在catch块中也可定义局部变量,该变量在catch块外不可用。

接下来通过一个案例来学习检测、抛出、捕获异常的流程,如例1所示。

例1

 1    #include <iostream>
 2    #include <string>
 3    using namespace std;
 4    
 5    int main()
 6    {
 7        int int_n1, int_n2;                                //定义两个整型变量
 8        cout << "Please input two integers:";          //输入提示
 9        cin >> int_n1 >> int_n2;                          //输入两个整数
 10        try
 11        {                                         
 12            cout << "Maybe exception code:" << endl;  //提示可能出现异常的代码
 13            if (int_n2 == 0)                               //除数为0则抛出异常
 14            {
 15                throw 0;
 16            }
 17            else                                                                           
 18            {
 19                cout << int_n1 << "/" << int_n2 << " = " 
 20                        << (int_n1 / int_n2) << endl;     //除数非零显示相除结果
 21            }
 22        }
 23        catch(int)                                           //捕捉参数为整型的异常
 24        {
 25            cout << "exception:div 0!" << endl;        //异常处理代码
 26        }
 27         system("pause");
 28        return 0;
 29    }

若输入的除数为0,则程序的运行结果如图1所示。

图1 例1运行结果

例1中异常处理的基本结构如代码第10-26行所示。第10-22行对可能产生异常的代码使用try块进行测试,若除数为0则通过第15行的throw抛出异常。throw后面是整数0,抛出了int类型的异常,try块后catch处理的正是int类型的异常,则显示相应的异常处理信息。

抛出异常和捕捉异常的代码通常会封装在不同的模块中,因为包含有抛出异常的代码会实现某个功能,为了代码的复用,将抛出异常的代码封装为独立的模块,与捕捉异常的代码实现了分离。例如,可将例7-2中的整数相除操作封装为独立函数,则抛出、捕获异常会分列在不同模块,通过try、throw、catch结构实现了不同模块间的通信。

接下来将7-2中的整数相除操作封装成函数,如例2所示。

例2

 1    #include <iostream>
 2    #include <string>
 3    using namespace std;
 4    
 5    int int_div(int a, int b)                                //实现整数相除的函数
 6    {
 7        if (b == 0){                                           //若除数为0,抛出异常
 8            throw 0;
 9        }
 10        return a / b;
 11    }
 12    int main()
 13    {
 14        int int_n1, int_n2;                                   //定义两个整型变量
 15        while (1){                                              //循环多次读取数据,完成相除操作
 16            cout << "Please input two integers:";
 17            cin >> int_n1 >> int_n2;                         //输入两个整型数
 18            try    {
 19                cout << "Maybe exception code:" << endl;
 20                cout << int_n1 << "/" << int_n2 << " = "
 21                    << int_div(int_n1, int_n2) << endl;  //除数非零显示相除结果
 22            }
 23            catch (int)                                        //捕捉整型异常
 24            {
 25                cout << "exception:div 0!" << endl;      //异常处理代码
 26            }
 27        }
 28        system("pause");
 29        return 0;
 30    }

程序运行结果如图2所示。

图2 例2运行结果

例2中代码第5-11行的int_div()函数实现了整数相除,若除数为0则抛出整数异常。main()函数中通过循环结构完成多对数据的相除操作,在第18-26行测试并捕捉异常,本例中try结构没有直接包含throw语句,而是包含了可能抛出异常的函数调用int_div()。

从运行结果分析抛出异常、捕捉异常结构分离的程序,其执行流程为:

通过try块中调用含有throw语句的函数int_div(),若出现异常,由于int_div()函数中不包含捕获异常的代码,程序从int_div()函数返回,不再执行throw语句后续内容。控制权从int_div()函数返回到其调用者main()函数中,在main()函数中寻找类型匹配的catch块,执行完相应的catch块后,执行main()函数剩余语句。

若int_div()函数没有抛出异常,从该函数返回main()函数后,忽略catch块,执行后续代码。

点击此处
隐藏目录