• 【C++】异常处理(一)


    这篇文章介绍下 C++ 的异常处理

    讨论一种最为常见的出现异常的情况,即 0 不能作为除数。为此,我们将自定义一个除法:

    #include 
    
    using namespace std;
    
    int divide(int a, int b)
    {
        return a / b;
    }
    
    int main()
    {
        int a = 3, b = 0;
        int res = divide(a, b);
        cout << "result = " << res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    事实上这样的代码在我的电脑上运行会卡在终端,也没有提示无法运行的报错,把 res 直接改成 3/0 就看到正常的报错了:

    TestDivide.cpp: In function 'int main()':
    TestDivide.cpp:14:29: warning: division by zero [-Wdiv-by-zero]
       14 |     cout << "result = " << 2/0;
          |                            ~^~
    result = 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    不使用异常机制的处理

    abort

    尝试在函数内部加入针对除数为0的报错,使用 abort()

    int divide(int a, int b)
    {
        if(b==0)
        {
            cout << "cannot divide by 0" << endl;
            abort();
        }
        return a / b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    事实上,这次除了打印我加入的一行文字以外,程序的运行并没有明显的变化。abort() 的作用是向标准错误流发送消息 abnormal program termination ,然后终止程序。它还会返回一个值告诉父进程处理失败。

    不知道为什么,这里没有终止进程。

    返回值处理错误

    另一种在 C++ 里常用的方式是,将我们想获取的值作为引用传回来,将返回值作为错误的标记。重新设计的 divide 如下:

    bool divide(int a, int b, int& res)
    {
        if(b==0)
        {
            cout << "cannot divide by zero" << endl;
            return false;
        }else
        {
            res = a/b;
            return true;   
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    调用我就不写了。

    这种方式一定程度上规避掉了系统的异常机制,换句话说,这是由开发者自己“实现”的异常,编译器不会认为这段代码是处理异常的。

    使用异常机制的处理

    现代C++ 的异常机制多少借鉴了其他语言的机制,即 throw-try-catch-finally 机制,即引发异常-捕获异常-处理异常

    引发异常

    改造 divide 函数,使其能够抛出异常:

    int divide(int a, int b)
    {
        if(b==0)
        {
            throw "cannot divide by ZERO!";
        }
        return a / b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    执行 throw 相当于返回了异常,因此从某种程度上来说,这里的 throwreturn 的作用差不多。但是不同的是,throw并不是把控制权返回给调用程序,而是会沿着调用序列后退,直到找到能处理异常的 catch 语句为止。

    捕获与处理异常

    main 函数改写如下:

    int main()
    {
        int a = 3, b = 0;
        int res = 0;
        try
        {
            res = divide(a, b);
        }
        catch (const char *s)
        {
            cout << s << endl;
        }
    
        cout << "res = " << res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    输出如下:

    cannot divide by ZERO!
    res = 0
    
    • 1
    • 2

    如果引发的异常最终没有与之匹配的类型 catch,效果等同于调用 abort() 函数。

    使用基于自定义类的异常机制

    使用字符串处理异常需要我们编写代码打印异常类型,依据面向对象的原则,其实写一个类来处理更好。

    在头文件中定义一个 bad_divide 类专门用于处理异常,其 what() 方法(此方法和 C++ 标准类中的方法在名字上是一致的)用于打印异常信息:

    struct bad_divide
    {
    private:
        int a;
        int b;
    
    public:
        bad_divide(int a, int b) : a(a), b(b){};
        void what()
        {
            cout << a << " divided by " << b << " is illegal!" << endl;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    同样,调用的地方也不再捕捉字符串了,而是捕捉这个对象,并调用 what() 方法打印异常。

    int divide(int a, int b)
    {
        if (b == 0)
        {
            throw bad_divide(a, b);
        }
        return a / b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    int main()
    {
        int a = 3, b = 0;
        int res = 0;
        try
        {
            res = divide(a, b);
        }
        catch (bad_divide &bd)
        {
            bd.what();
        }
    
        cout << "res = " << res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    Vue3中自定义事件实现子组件向父组件传递数据
    批量提取图片文件名使用Python中os.listdir( )函数时出现文件名乱序,非预想排序的解决方法
    如何将现有的`Blazor`项目的主题切换写的更好看?
    XSS靶场-DOM型初级关卡
    Java高级篇-----jdk1.8新特性
    DHCP动态获取IP地址流程
    基于android的流动人口管理移动APP-计算机毕业设计
    1210、MHA集群
    GO 协程【VS】C# 多线程【Go-C# Round 1】
    Linux:Mac VMware Fusion13以及CentOS7安装包
  • 原文地址:https://blog.csdn.net/qq_37387199/article/details/133244461