day09(C++)异常处理

目录

异常处理

1. 异常的概念

2. throw 抛出异常

3. try-catch 捕获异常

4. 标准异常

5. 捕获基类异常

6. 多重捕获


异常处理

1. 异常的概念

异常是程序在运行期间产生的问题,而非编译期间产生的问题,因此程序的语法没有问题,但是逻辑出现了问题。

异常本质上提供的是一种程序控制权转移的方式,程序一旦出现异常没有经过正确的处理,就会造成程序运行的崩溃。

处理异常的方式有两种:

  • 抛出异常 throw
  • 捕获异常 try-catch

2. throw 抛出异常

throw语句抛出的是通常是一个对象,对象的类型就是异常的类型,throw可以把这个对象抛出到调用的上一级。如果上一级没有正确的处理异常,异常对象会逐级向上反馈,如果在主函数中仍然没有正确处理异常,程序运行崩溃。

#include <iostream>

using namespace std;

/**
 * @brief divide 除法
 * @param a 被除数
 * @param b 除数
 * @return 商
 */
double divide(double a,double b)
{
    if(b == 0)
        throw "除数为0!!!";
    return a/b;
}

int main()
{
    cout << divide(4,5) << endl;
    cout << divide(6,7) << endl;
    cout << divide(1,0) << endl; // 运行终止

    cout << "主函数结束" << endl;
    return 0;
}

3. try-catch 捕获异常

在try块中放置可能抛出异常的代码,在catch代码中对异常类型进行匹配:

  • 如果匹配成功,程序从try块抛出异常的位置直接跳转到catch块,执行弥补错误的代码。执行完catch块后,try-catch结束。
#include <iostream>

using namespace std;

/**
 * @brief divide 除法
 * @param a 被除数
 * @param b 除数
 * @return 商
 */
double divide(double a,double b)
{
    if(b == 0)
        throw "除数为0!!!";
    return a/b;
}

int main()
{
    cout << divide(4,5) << endl;
    try
    {
       cout << divide(1,0) << endl;
       cout << divide(6,7) << endl; // 没有执行的机会
    }catch(const char* e)
    {
        cout << e << endl;
        // 弥补的措施
    }

    cout << "主函数结束" << endl;
    return 0;
}

  • 如果匹配失败,异常会交给上一级处理,直到主函数。
#include <iostream>

using namespace std;

/**
 * @brief divide 除法
 * @param a 被除数
 * @param b 除数
 * @return 商
 */
double divide(double a,double b)
{
    if(b == 0)
        throw "除数为0!!!";
    return a/b;
}

void test()
{
    try
    {
       cout << divide(1,0) << endl;
       cout << divide(6,7) << endl; // 没有执行的机会
    }catch(string e) // 匹配失败
    {
        cout << e << endl;
        // 弥补的措施
    }
}

int main()
{
    cout << divide(4,5) << endl;
    try
    {
        test();
    }catch(const char* e) // 最后一次机会捕获成功
    {
        cout << e << endl;
        // 弥补措施
    }

    cout << "主函数结束" << endl;
    return 0;
}

4. 标准异常

C++为常见的异常类型进行了分类,如下所示。

使用上面的异常类型需要引入头文件 #include <stdexcept>

程序员自定义异常最好通过继承加入到上面的标准异常家族中,方便后续处理和管理。

#include <iostream>
#include <stdexcept> // 头文件

using namespace std;

// 自定义异常类型
class ZeroException:public exception
{
public:
    // throw() 是异常规格说明,表示此函数不会抛出异常
    const char* what() const throw()
    {
        return "除数为0!!!";
    }
};



/**
 * @brief divide 除法
 * @param a 被除数
 * @param b 除数
 * @return 商
 */
double divide(double a,double b)
{
    if(b == 0)
        throw ZeroException();
    return a/b;
}

int main()
{
    cout << divide(4,5) << endl;
    try
    {
       cout << divide(1,0) << endl;
       cout << divide(6,7) << endl; // 没有执行的机会
    }catch(const ZeroException& e)
    {
        cout << e.what() << endl;
        // 弥补的措施
    }

    cout << "主函数结束" << endl;
    return 0;
}

5. 捕获基类异常

可以捕获基类异常,同时匹配所有派生类异常类型。

#include <iostream>
#include <stdexcept> // 头文件

using namespace std;

class Animal
{
public:
    string a = "Animal";

    virtual void eat()
    {
        cout << "吃东西" << endl;
    }
};

class Monkey:public Animal
{
public:
    string b = "Monkey";

    virtual void eat()
    {
        cout << "吃香蕉" << endl;
    }
};




int main()
{
    string s = "fsdjkfgsdhj";

    int err_id;
    cin >> err_id;
    try
    {
        if(err_id%3 == 0) // 可以捕获
            cout << s.at(348568734) << endl;
        else if(err_id%3 == 2) // 可以捕获
            throw length_error("长度错误");
        else // 不能捕获
        {
            Animal a1;
            Monkey& m2 = dynamic_cast<Monkey&>(a1);
        }
    }catch(logic_error& e)
    {
        cout << e.what() << endl;
        cout << "弥补措施" << endl;
    }

    cout << "主函数结束" << endl;
    return 0;
}

6. 多重捕获

在上面的代码中,虽然可以同时处理多种异常类型,但是是牺牲捕获精度实现的,为了保证捕获的精度,可以使用多重捕获。

在使用多重捕获时,一定要先catch派生类异常类型,再catch基类异常类型。

#include <iostream>
#include <stdexcept> // 头文件

using namespace std;

class Animal
{
public:
    string a = "Animal";

    virtual void eat()
    {
        cout << "吃东西" << endl;
    }
};

class Monkey:public Animal
{
public:
    string b = "Monkey";

    virtual void eat()
    {
        cout << "吃香蕉" << endl;
    }
};




int main()
{
    string s = "fsdjkfgsdhj";

    int err_id;
    cin >> err_id;
    try
    {
        if(err_id%3 == 0) // 可以捕获
            cout << s.at(348568734) << endl;
        else if(err_id%3 == 2) // 可以捕获
            throw length_error("长度错误");
        else // 不能捕获
        {
            Animal a1;
            Monkey& m2 = dynamic_cast<Monkey&>(a1);
        }
    }
    catch(out_of_range& e)
    {
        cout << e.what() << endl;
        cout << "分支1" << endl;
    }
    catch(length_error& e)
    {
        cout << e.what() << endl;
        cout << "分支2" << endl;
    }
    catch(logic_error& e)
    {
        cout << e.what() << endl;
        cout << "保底1" << endl;
    }
    catch(exception& e)
    {
        cout << e.what() << endl;
        cout << "保底2" << endl;
    }

    cout << "主函数结束" << endl;
    return 0;
}

C++的异常处理机制并不完善,是否使用取决于所在的开发团队和技术框架。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值