zoukankan      html  css  js  c++  java
  • C++异常处理机制(throw、try、catch、finally)

    一、什么是异常处理

      一句话:异常处理就是处理程序中的错误。

    程序运行时常会碰到一些异常情况,例如:

    1、做除法的时候除数为 0;

    2、用户输入年龄时输入了一个负数;

    3、用 new 运算符动态分配空间时,空间不够导致无法分配;

    4、访问数组元素时,下标越界;打开文件读取时,文件不存在。


    这些异常情况,如果不能发现并加以处理,很可能会导致程序崩溃。

     

     

    二、异常处理机制

    1、当发生异常,程序无法沿着正常的顺序执行下去的时候,立即结束程序可能并不妥当。我们需要给程序提供另外一条可以安全退出的路径,

    在结束前做一些必要的工作,如将内存中的数据写入文件、关闭打开的文件、释放动态分配的内存空间等。

     

    2、当发生异常的时候,程序马上处理可能并不妥当(一个异常有多种处理方法,或者自己无法处理异常),需要将这个异常抛出给他的上级(直接调用者),

    由上级决定如何处理。或者是自己不处理再转交给它的上级去处理,一直可以转交到最外层的main()函数

     

    3、另外,异常的分散处理不利于代码的维护,尤其是对于在不同地方发生的同一种异常,都要编写相同的处理代码也是一种不必要的重复和冗余。

    如果能在发生各种异常时让程序都执行到同一个地方,这个地方能够对异常进行集中处理,则程序就会更容易编写、维护。

     

    在引入异常处理机制之前,异常的处理方式有两种方法

      1、使用整型的返回值标识错误;

      2、使用errno宏(可以简单的理解为一个全局整型变量)去记录错误。当然C++中仍然是可以用这两种方法的。

            这两种方法最大的缺陷就是会出现不一致问题。例如有些函数返回1表示成功,返回0表示出错;而有些函数返回0表示成功,返回非0表示出错。

            还有一个缺点就是函数的返回值只有一个,你通过函数的返回值表示错误代码,那么函数就不能返回其他的值。

     

    鉴于上述原因,C++引入了异常处理机制

     

    异常处理流程

    C++ 异常处理涉及到三个关键字:try、catch、throw

    1、throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。

    2、try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

    3、catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。

    4、finally:关键字finally放在catch之后,如果异常没有被catch捕获,会使用关键字去清理释放资源

      如果有一个块抛出一个异常,捕获异常的方法会使用 trycatch 关键字。try 块中放置可能抛出异常的代码(判断异常的类型),try 块中的代码被称为保护代码。

      catch后面对应每个异常的处理方法。

      以除法除0举例。代码如下所示:

      

    #include <iostream>
    using namespace std;
    
    double division(int a, int b)
    {
        if (b == 0)
        {
            throw "Division by zero condition!";
        }
        return (a / b);
    }
    
    int main()
    {
        int x = 50;
        int y = 0;
        double z = 0;
        //trycatch的使用和switchcase的使用类似
        try {
            z = division(x, y);
            cout << z << endl;
        }
        catch (const char* msg) {
            cerr << msg << endl;
        }
       //finally{}
        return 0;
    }

    三、几个概念

    1、栈展开

    栈展开指的是:当异常抛出后,匹配catch的过程。

           抛出异常时,将暂停当前函数的执行,开始查找匹配的catch子句。沿着函数的嵌套调用链向上查找,直到找到一个匹配的catch子句,或者找不到匹配的catch子句。

    栈展开的时候,会通过析构函数或者是delete销毁局部对象(从开始匹配位置到确认匹配这一段中间位置的资源会被释放)

     

     

    2、析构函数应该从不抛出异常。

    如果析构函数中出现异常,那么就应该在析构函数内部将这个异常进行处理,而不是将异常抛出去。

    为什么不应该?抛出异常的就是栈展开的过程,而栈展开会调用析构函数销毁局部对象,这样多次调用析构函数会导致程序崩溃(内存泄漏)

     

    3、构造函数可以抛出异常

      当构造函数内出现异常,可以选择将异常抛出,在栈展开的过程调用析构函数释放已申请的内存,也可以在内部将异常处理,手动调用delete释放

    4、catch捕获所有异常

    语法:在catch语句中,使用三个点(…)。即写成:catch (…)   这里三个点是“通配符”,类似 可变长形式参数。

  • 相关阅读:
    Padding和父子继承宽高之间的关系
    Js实例——模态框弹出层
    Java——异常谜题
    BZOJ 2743 【HEOI2012】 采花
    BZOJ 4614 【WF2016】 Oil
    BZOJ 1004 【HNOI2008】 Cards
    codevs 2495 水叮当的舞步
    BZOJ 1227 【SDOI2009】 虔诚的墓主人
    BZOJ 3505 【CQOI2014】 数三角形
    BZOJ 4423 【AMPPZ2013】 Bytehattan
  • 原文地址:https://www.cnblogs.com/-citywall123/p/12901301.html
Copyright © 2011-2022 走看看