C++ 异常处理机制
2020 年 06 月 30 日 233 1364 字 暂无评论

前言

  1. 异常是一种程序控制机制,与函数机制独立和互补

    1. 函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈结构,却可以同时设置多个异常类型作为网捕条件,从而以类型匹配在栈机制中跳跃回馈。
  2. 异常设计目的:

    1. 栈机制是一种高度节律性控制机制,面向对象编程却要求对象之间有方向、有目的的控制传动,从一开始,异常就是冲着改变程序控制结构,以适应面向对象程序更有效地工作这个主题,而不是仅为了进行错误处理。
    2. 异常设计出来之后,却发现在错误处理方面获得了最大的好处。

01.异常处理的基本思想

  1. C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。
  2. 异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如下图

  1. 异常超脱于函数机制,决定了其对函数的跨越式回跳。
  2. 异常跨越函数

02.C++异常处理的实现

2.1 异常基本语法

  1. 若有异常则通过throw操作创建一个异常对象并抛掷。
  2. 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。
  3. 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。
  4. catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。
  5. 如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。
  6. 处理不了的异常,可以在catch的最后一个分支,使用throw语法,向上扔。
  7. 异常机制与函数机制互不干涉,但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题。
  8. catch代码块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。
  9. 异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据

2.2案例

  • 被零整除
#include <iostream>

using namespace std;

int divide(int x, int y)
{
    if (y == 0)
    {
        throw x;
    }
    return x / y;
}

int main()
{
    try
    {
        cout << "8/2 = " << divide(8, 2) << endl;
        cout << "10/0 =" << divide(10, 0) << endl;
    }
    catch (int e)
    {
        cout << "e" << " is divided by zero!" << endl;
    }
    catch (...)
    {
        cout << "未知异常" << endl;
    }

    cout << "ok" << endl;
    return 0;
}
  • 运行结果
8/2 = 4
e is divided by zero!
ok

2.3栈解旋(unwinding)

  • 异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。
  • 代码案例
#include <iostream>

using namespace std;

class MyException {};

class Test
{
public:
        Test(int a = 0, int b = 0)
        {
                this->a = a;
                this->b = b;
                cout << "Test 构造函数执行" << "a:" << a << " b: " << b << endl;
        }
        void printT()
        {
                cout << "a:" << a << " b: " << b << endl;
        }
        ~Test()
        {
                cout << "Test 析构函数执行" << "a:" << a << " b: " << b << endl;
        }
private:
        int a;
        int b;
};

void myFunc() throw (MyException)
{
        Test t1(1,1);
        Test t2(2,3);

        cout << "定义了两个栈变量,异常抛出后测试栈变量的如何被析构" << endl;

        throw MyException();
}


int main()
{
        //异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,
        //都会被自动析构。析构的顺序与构造的顺序相反。
        //这一过程称为栈的解旋(unwinding)
        try
        {
                myFunc();
        }
        //catch(MyException &e) //这里不能访问异常对象
        catch (MyException) //这里不能访问异常对象
        {
                cout << "接收到MyException类型异常" << endl;
        }
        catch (...)
        {
                cout << "未知类型异常" << endl;
        }

        return 0;
}
  • 运行结果
Test 构造函数执行a:1 b: 1
Test 构造函数执行a:2 b: 3
定义了两个栈变量,异常抛出后测试栈变量的如何被析构
Test 析构函数执行a:2 b: 3
Test 析构函数执行a:1 b: 1
接收到MyException类型异常

2.4 异常接口声明

  1. 为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型,例如:void func() throw (A, B, C , D); //这个函数func()能够且只能抛出类型A B C D及其子类型的异常
  2. 如果在函数声明中没有包含异常接口声明,则次函数可以抛掷任何类型的异常,例如:void func();
  3. 一个不抛掷任何类型异常的函数可以声明为:void func() throw();
  4. 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序。

2.5 异常类型和异常变量的生命周期

  1. throw的异常是有类型的,可以使,数字、字符串、类对象。
  2. throw的异常是有类型的,catch严格按照类型进行匹配。
  3. 注意 异常对象的内存模型

03.标准程序库异常

  • 案例
#include <iostream>
#include <stdexcept>

using namespace std;

class Teacher
{
public:
        Teacher(int age)  //构造函数, 通过异常机制 处理错误
        {
                if (age > 100)
                {
                        throw out_of_range("年龄太大");
                }
                this->age = age;
        }
protected:
private:
        int age;
};

int main()
{
        try
        {
                Teacher t1(102);
        }
        catch (out_of_range e)
        {
                cout << e.what() << endl;
        }
        exception e;
        return 0;
}
  • 运行结果
年龄太大

版权属于:zfh

本文链接:http://zfhblog.com/index.php/archives/95/



评论已关闭