12、C++:异常处理

开发软件一般按照正常的流程操作时运行不会出问题,但是用户不一定会根据软件工程师的想法来操作软件,而且往往随机性很大,另外,软件的运行环境也会改变,例如硬盘空间不足、文件被移走,这些都可能会导致软件出现异常,甚至崩溃。所以我们进行软件开发时要充分考虑异常的捕捉和处理。

基本思想

  • 目标:
    使软件具有容错能力,在出现运行环境或者异常操作等问题时,程序能够继续往下运行,必要时弹出提示信息。
  • 流程
    软件开发中往往每个函数都有自己的分工,很多出现错误的函数都不会处理错误,而是产生一个异常让调用者捕捉和处理。如果调用者也不能处理此异常,则异常就会被继续向上级调用者传递,这个传递过程会一直持续到异常能被处理为止。如果程序最终没能处理这个异常,那么它就会被传递给C++的库函数terminate,然后terminate会调用abort函数终止程序。

语法

异常处理机制是靠try、throw和catch语句实现的。

  1. throw语句
throw 表达式

当某段程序有了不能处理的异常时,就可以用“throw 表达式”的方式传递这个异常给调用者。这里throw后的表达式在语法上与return后的表达式类似。

  1. try块的语法形式
try
{
    复合语句
}

子句后括号里的复合语句就是被监测的程序段。如果某段程序或者调用的某个函数可能会产生异常,就把它放到try后。当try子句后的程序段发生异常时,程序段中throw就会抛出这个异常。
如果try监测的某段程序多个地方需要抛出异常,那么throw后应该跟不同类型的表达式来区分,而不应该只通过不同的值区分。

  1. catch语句
try
{
    复合语句
}
catch(异常类型的声明)
{
    复合语句
}
catch(异常类型的声明)
{
    复合语句
}

try的catch子句,catch子句后括号里的异常类型的声明,在语法上与函数的形参类似,可以是某个类型(包括类)的值也可以是引用,它指明了此catch子句用来处理何种类型的异常。当try子句中的异常抛出后,每个catch子句会被依次检查,哪个catch子句的异常类型的声明与抛出异常的类型一致,就由哪个catch子句来处理此异常。catch后异常类型的声明部分可以是一个省略号,形式如:catch(…),这种形式的catch子句可以处理任何类型的异常,它只能放到try块所有其他catch语句之后

异常处理的执行过程

  1. 程序正常执行到try块的try子句,然后执行try子句后的复合语句,也就是被监测的程序段。
  2. 如果try子句后的程序段正常执行了,没有发生任何异常,那么此try块的所有catch子句将不被执行,程序直接跳转到整个try块(包括try子句和所有catch子句)后继续执行。
  3. 如果try子句后的程序段或者此程序段中的任何调用函数发生了异常,并通过throw抛出了这个异常,则此try块的所有catch子句会按其出现的顺序被检查。若没有找到匹配的处理程序则继续检查外层的try块。如果一直找不到则此过程会继续到最外层的try块被检查。
    1. 找到了匹配的处理程序,则相应catch子句捕捉异常,将异常对象拷贝给catch的参数,如果此参数是引用则它指向异常对象。catch的参数被这样初始化以后,此catch子句对应的try子句后的程序段中,从开头到异常抛出位置之间构造的所有对象进行析构,析构顺序与构造顺序相反。然后catch处理程序被执行,最后程序跳转到try块之后的语句执行。
    2. 始终没有找到匹配的处理程序,则运行C++库函数terminate,而terminate函数调用abort函数终止程序。
#include <iostream>
#include "math.h"
using namespace std;
double GetSqrt(double x);       // 求平方根的函数的原型声明
int main()
{
    try
    {
        // 由于求平方根运算有可能出现对负数运算的异常,所以放到try块中
        cout << "9.0的平方根是 " << GetSqrt(9.0) << endl;
        cout << "-1.0的平方根是 " << GetSqrt(-1.0) << endl;
        cout << "16.0的平方根是 " << GetSqrt(16.0) << endl;
    }
    catch (double y)    // 捕捉double型异常
    {
        cout << "发生对负数" << y << "求平方根的异常。" << endl;
    }
    cout << "程序继续运行完毕。" << endl;
    return 0;
}
double GetSqrt(double x)
{
    if (x < 0)
        throw x;     // 如果x为负数,则抛出一个double型异常
    return sqrt(x);
}

在这里插入图片描述

  • 根据结果可以看出,程序在运行cout << “-1.0的平方根是 " << GetSqrt(-1.0) << endl;时GetSqrt函数抛出异常,异常被main函数中catch子句捕捉,输出信息后,程序跳转到main函数最后一句输出"程序继续运行完毕。”。而try子句后的cout << "16.0的平方根是 " << GetSqrt(16.0) << endl;没有被执行。这是因为异常抛出后会按照catch子句出现的顺序依次检查,当找到匹配的catch处理程序时后面的所有catch子句就被忽略。根据这个原理,若catch(…)放到前面则其后的所有catch子句就不会被检查,因此它只能放到try块的最后。
  • 其实很多情况下catch子句的处理程序并不需要访问异常对象,只需要声明异常的类型就够了,例如上面程序中的catch子句就可以改成:
catch(double)    // 捕捉double型异常
{
    cout << "发生对负数求平方根的异常。" << endl;
}

当然如果需要访问异常对象就要给出参数名,就像上面程序中的catch(double y)。

异常接口声明

  1. 在函数的声明中给出它可能会抛出的所有异常类型。例如:
void func()  throw(X, Y);
  1. 若函数的声明中没有给出任何异常接口声明,则此函数可能抛出任何类型的异常。例如:
void func();
  1. 如果函数不抛出任何类型的异常,则可以这样声明:
void func()  throw();
  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
鸡啄米的资料,大家可以上官网看。这是我整理的压缩包,大家可以离线看。(这个资源还要大家积分,实在是惭愧,只因积分不够用,抱歉,体谅!) 大家要是觉得不错,可以下载我整理得另一套资源:VS2010+MFC编程入门,也是鸡啄米的,写得不错,通俗易懂! --------------------------完整目录如下------------------- 第一部分:C++编程概述 鸡啄米:C++编程入门系列之前言 鸡啄米:C++编程入门系列之一(进制数) 鸡啄米:C++编程入门系列之二(原码、反码与补码) 第二部分:C++简单程序设计 鸡啄米:C++编程入门系列之三(VS2010的使用介绍) 鸡啄米:C++编程入门系列之四(数据类型) 鸡啄米:C++编程入门系列之五(运算符和表达式) 鸡啄米:C++编程入门系列之六(算法的基本控制结构之选择结构) 鸡啄米:C++编程入门系列之七(算法的基本控制结构之循环结构) 鸡啄米:C++编程入门系列之八(自定义数据类型) 第三部分:函数 鸡啄米:C++编程入门系列之九(函数的定义与调用) 鸡啄米:C++编程入门系列之十(函数的参数传递与内联函数) 鸡啄米:C++编程入门系列之十一(重载函数与函数模板) 第四部分:类与对象 鸡啄米:C++编程入门系列之十二(类与对象:面向对象设计的基本思 想和特点) 鸡啄米:C++编程入门系列之十三(类与对象:类的声明、成员的访问 控制和对象) 鸡啄米:C++编程入门系列之十四(类与对象:构造函数和析构函数) 鸡啄米:C++编程入门系列之十五(类与对象:类的组合) 鸡啄米:C++编程入门系列之十六(类与对象:类模板) 鸡啄米:C++编程入门系列之十七(类与对象:UML简介) 第五部分:C++程序设计必知 鸡啄米:C++编程入门系列之十八(C++程序设计必知:作用域和可见 性) 鸡啄米:C++编程入门系列之十九(C++程序设计必知:生存期) 鸡啄米:C++编程入门系列之二十(C++程序设计必知:数据和函数) 鸡啄米:C++编程入门系列之二十一(C++程序设计必知:类的静态成 员) 鸡啄米:C++编程入门系列之二十二C++程序设计必知:友元) 鸡啄米:C++编程入门系列之二十三(C++程序设计必知:常引用、常对 象和对象的常成员) 鸡啄米:C++编程入门系列之二十四(C++程序设计必知:多文件结构和 编译预处理命令) 第六部分:数组、指针和字符串 鸡啄米:C++编程入门系列之二十五(数组、指针和字符串:数组的声 明和使用) 鸡啄米:C++编程入门系列之二十六(数组、指针和字符串:数组的存 储与初始化、对象数组、数组作为函数参数) 鸡啄米:C++编程入门系列之二十七(数组、指针和字符串:指针变量 的声明、地址相关运算--“*”和“&”) 鸡啄米:C++编程入门系列之二十八(数组、指针和字符串:指针的赋 值和指针运算) 鸡啄米:C++编程入门系列之二十九(数组、指针和字符串:指向数组 元素的指针和指针数组) 鸡啄米:C++编程入门系列之三十(数组、指针和字符串:指针用作函 数参数、指针型函数和函数指针) 鸡啄米:C++编程入门系列之三十一(数组、指针和字符串:对象指 针) 鸡啄米:C++编程入门系列之三十二(数组、指针和字符串:动态内存 分配和释放) 鸡啄米:C++编程入门系列之三十三(数组、指针和字符串:用字符数 组存放和处理字符串) 鸡啄米:C++编程入门系列之三十四(数组、指针和字符串:string 类) 第七部分:继承与派生 鸡啄米:C++编程入门系列之三十五(继承与派生:概念介绍与派生类 的声明) 鸡啄米:C++编程入门系列之三十六(继承与派生:派生类从基类继承 的过程) 鸡啄米:C++编程入门系列之三十七(继承与派生:派生类对基类成员 的访问控制之公有继承) 鸡啄米:C++编程入门系列之三十八(继承与派生:派生类对基类成员 的访问控制之保护继承与私有继承) 鸡啄米:C++编程入门系列之三十九(继承与派生:派生类的构造函 数) 鸡啄米:C++编程入门系列之四十(继承与派生:派生类的析构函数) 鸡啄米:C++编程入门系列之四十一(继承与派生:作用域分辨符) 鸡啄米:C++编程入门系列之四十二(继承与派生:虚基类及其派生类 的构造函数) 鸡啄米:C++编程入门系列之四十三(继承与派生:赋值兼容规则) 第八部分:多态性 鸡啄米:C++编程入门系列之四十四(多态性:多态的概念和类型) 鸡啄米:C++编程入门系列之四十五(多态性:运算符重载的概念和规 则) 鸡啄米:C++编程入门系列之四十六(多态性:运算符重载为类的成员 函数) 鸡啄米:C++编程入门系列之四十七(多态性:运算符重载为类的友元 函数) 鸡啄米:C++编程入门系列之四十八(多态性:虚函数) 鸡啄米:C++编程入门系列之四十九(多态性:纯虚函数和抽象类) 第九部分:异常处理 鸡啄米:C++编程入门系列之五十(异常处理) 鸡啄米:C++编程入门系列之目录和总结

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值