C++【12】(异常处理机制)

299 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、异常处理机制

1.1 相关概念

面向对象一般都有异常处理机制,异常处理就是处理程序中的错误。所谓错误是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等)。

1.2 语法格式

检测异常:try语句块,将还有抛出异常的代码放在try语句块中进行检测

try{
}

抛出异常:throw关键字,当执行了throw之后,try语句块就会立即结束,运行后面处理异常的代码

throw 常量/表达式/变量

处理异常:catch函数,当抛出异常之后,立即执行当前函数内部的代码,catch函数参数的类型要与throw抛出的类型一致

catch(执行类型的引用)
{
}

1.3 用户自定义异常

#include <iostream>

using namespace std;

int MyDiv1(int m, int n)
{
    if(n == 0)
    {   //C语言中没有异常处理机制,所以返回-1表示出错
        return -1;
    }
    else
    {
        return m / n;
    }
}

void test1()
{
    //int num = MyDiv2(10, 3);
    //int num = MyDiv2(10, 0);
    int num = MyDiv1(10, -10);
    if(num == -1)
    {
        cout << "出错:除数为0" << endl;
    }
    else
    {
        cout << num << endl;
    }
}

//异常处理机制的简单使用
void test2()
{
    cout << "11111111111111111" << endl;

    try {
        cout << "hello world" << endl;

        //含有throw的代码要放在try当中,可以是代码也可以是函数
        //当执行到throw,try语句块就会立即结束执行catch里面的代码
        throw 10.345f;

        cout << "nihao beijing" << endl;
    }
    //try后面必须紧跟catch,否则报错
    //cout << "***************" << endl;
    //catch函数的参数类型要与throw抛出的值的类型一致
    catch (float &e) {
        cout << "111welcome to hqyj:" << e << endl;
    }
    catch (int &e) {
        cout << "222welcome to hqyj:" << e << endl;
    }


    cout << "22222222222222222" << endl;
}

//用户自定义异常
//当自己封装函数时,如果出现了异常,只抛出异常,
//而检测和处理异常的代码不在函数内部写,而是在调
//用的时候再去写
int MyDiv2(int m, int n)
{
    if(n == 0)
    {
        throw -1;
    }
    else
    {
        return m / n;
    }
}

void test3()
{
    int a = 10, b = 0;
    int num;

    try {
        num = MyDiv2(a, b);
        cout << a << "/" << b << "=" <<num << endl;
    } catch (int &e) {
        cout << "错误:除数为0" << endl;
    }

}

int main()
{
    test3();

    return 0;
}

练习:编写函数,获取数组指定元素的内容

#include <iostream>

using namespace std;

int getArray(int *a, int len, int n)
{
    if(n < 0 || n > len - 1)
    {
        throw 0;
    }
    else
    {
        return a[n];
    }
}

void test1()
{
    int arr[] = {100, 20, 5, 33, 89, 66, 91, 73, 52};
    int num;
    try {
        num = getArray(arr, sizeof(arr) / sizeof(int), 20);
        cout << num << endl;
    } catch (int &e) {
        if(e == 0)
        {
            cout << "数组元素访问越界" << endl;
        }
    }
}

int main()
{
    test1();

    return 0;
}

1.4 标准异常库

标准库中也提供了很多的异常类,它们是通过类继承组织起来的。异常类继承层级结构图如下:

https://note.youdao.com/yws/public/resource/d23ff061138554293b27d88e32be9553/xmlnote/CF93AEC286D048F28357AD28739C0875/884B8FD4EC52416F92C2BEB44BC2947A/84776

标准异常类的具体描述:

异常名称描述
exception所有标准异常类的父类
bad_alloc当operator new and operator new[],请求分配内存失败时
bad_exception这是个特殊的异常,如果函数的异常抛出列表里声明了bad_exception异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为bad_exception类型
bad_typeid使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常
bad_cast使用dynamic_cast转换引用失败的时候
ios_base::failureio操作过程出现错误
logic_error逻辑错误,可以在运行前检测的错误
runtime_error运行时错误,仅在运行时才可以检测的错误

logic_error的子类:

异常名称描述
length_error试图生成一个超出该类型最大长度的对象时,例如vector的resize操作
domain_error参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数
out_of_range超出有效范围
invalid_argument参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常

runtime_error的子类:

异常名称描述
range_error计算结果超出了有意义的值域范围
overflow_error算术计算上溢
underflow_error算术计算下溢
invalid_argument参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常
#include <iostream>
#include <exception>

using namespace std;

int getArray(int *a, int len, int n)
{
    
    if(n < 0 || n > len - 1)
    {   
        //out_of_range oor("数组元素访问越界");
        //throw oor;
        //可以使用匿名对象来返回
        throw out_of_range("数组元素访问越界");
    }
    else
    {
        return a[n];
    }
}

void test1()
{
    int arr[] = {100, 20, 5, 33, 89, 66, 91, 73, 52};
    int num;
    try {
        num = getArray(arr, sizeof(arr) / sizeof(int), 20);
        cout << num << endl;
    } 
    //当使用标准异常库时,如果不知道是哪个类,可以使用所有标准异常类的父类
    catch (exception &e) {
        cout << e.what() << endl;
    }
}

int main()
{
    test1();
    return 0;
}