66. 异常处理(下)

深入异常处理

有时在工程中只关心是否产生了异常,而不关心具体的异常类型,C++语言可以做到吗?

C++中的catch语句可以使用…捕获所有的异常: catch(...)

#include <cstdlib>
#include <iostream>

using namespace std;

int test(int i)
{
    if( i = = 1 )
    {
        throw "p";//字符串
    }
    
    if( i = = 2 )
    {
        throw 0.5;
    }
    
    if( i = = 3 )
    {
        throw 3;
    }
    
    if( i = = 4 )
    {
        throw 'c';//字符
    }
    
    return i;
}

int main(int argc, char *argv[])
{
    for(int i=0; i<10; i++)
    {
        try
        {
            cout<<test(i)<<endl;//test函数会产生异常,把它放在try语句里面
        }
        catch(char e)
        {   
            cout<<"Exception: "<<e<<endl;
        }
        catch(...)
        {
            cout<<"Exception Occur"<<endl;
        }
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
result:
0
Exception Occur
Exception Occur
Exception Occur
Exception: c
5
6
7
8
9
Press the enter key to continue ...

catch(…)可以捕获所有异常但却无法得到异常信息

catch(…)一般作为最后一个异常处理块出现

看见代码中的catch就要意识到这里在处理异常情况,而异常是在对应的try中产生的。

在catch语句块中仍然可以抛出异常

#include <cstdlib>
#include <iostream>

using namespace std;

int test(int i)
{
    if( (6 <= i) && (i <= 9) )
    {
        throw i;
    }
    
    return i;
}

int main(int argc, char *argv[])
{
    try
    {
        for(int i=0; i<10; i++)
        {
            try
            {
                cout<<test(i)<<endl;//for循环,正常打印0-5;i=6时扔出来一个异常
            }
            catch(int e)//i=6的异常被他接住
            {   
                cout<<"Exception: "<<e<<endl;
                
                throw e;//catch里又产生异常,找try,扔出去,再找try对应的catch
                //扔出的异常没有被捕获,程序就会死掉
            }
        }
    }
    catch(int e)//捕获!接住catch里的异常
    {
        cout<<"Catch: "<<e<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
result:
0
1
2
3
4
5
Exception: 6
Catch: 6
Press the enter key to continue ...

在catch(…)语句块中,可以通过不带参数的throw语句抛出捕获的异常

#include <cstdlib>
#include <iostream>

using namespace std;

int test(int i)
{
    if( (6 <= i) && (i <= 9) )
    {
        throw i;
    }
    
    return i;
}

int main(int argc, char *argv[])
{
    try
    {
        for(int i=0; i<10; i++)
        {
            try
            {
                cout<<test(i)<<endl;
            }
            catch(...)
            {   
                cout<<"Exception Occur"<<endl;
                
                throw;//扔出外层catch所捕获到的异常
            }
        }
    }
    catch(int e)//try捕获到的异常是整型6,正好匹配
    {
        cout<<"Catch: "<<e<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
result:
0
1
2
3
4
5
Exception Occur
Catch: 6
Press the enter key to continue ...

异常与对象

不要在构造函数中抛出异常

在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全(构造函数还没结束,不给你调用析构函数)

不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏

构造函数中的异常示例(构造函数不完全,导致未调用析构函数👇)

#include <cstdlib>
#include <iostream>

using namespace std;

class Test
{
    int* p;
public:
    Test()
    {
        cout<<"Test()"<<endl;
        
        p = new int[5];//创建一个包含5个整数的数组,并将该数组的首地址赋值给指针p。
      //delete[] p; // 正确释放数组内存

      //p = new int(5);//创建一个单一的整数对象,并将其初始化为值5,
      //delete p; // 正确释放单个对象的内存

        
        throw 10;
    }
    
    ~Test()
    {
        cout<<"~Test()"<<endl;
        delete[] p;
        //没调用,内存没释放,导致内存报废,内存泄露
    }
};

int main(int argc, char *argv[])
{
    try
    {
        Test t;
    }
    catch(int e)
    {
        cout<<"Catch: "<<e<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
result:
Test()
Catch: 10
Press the enter key to continue ...

工程中的异常应用

  • 在工程中会定义一系列的异常类(工程中抛出异常类对象)

  • 通过继承,可以得到一个异常类族

  • 每个类代表工程中可能出现的一种异常类型

  • 由于对象构造与拷贝的开销,在定义catch语句块使用引用作为参数

标准库中的异常

  • 在工程中可以使用标准库中的异常类

  • 可将标准库中的异常类作为基类派生新的异常类

  • 标准库中的异常都是从exception类派生的

  • exception类有两个主要的分支

    • logic_error用于描述程序中出现的逻辑错误

      • 如:传递无效参数

    • runtime_error用于描述无法预料的事件所造成的错误

      • 如: 内存耗尽,硬件错误

logic_error和runtime_error都提供了一个参数为字符串的构造函数,这样就可以保持错误信息

通过what()成员函数就可以得到错误的信息

#include <cstdlib>
#include <iostream>
#include <stdexcept>//异常头文件

using namespace std;

class divide_by_zero : public logic_error
{
public:
    divide_by_zero(const char* s) : logic_error(s)//初始化列表
    {
    }
};

double Div(double a, double b)
{
    if( (-0.00000001 < b) && ( b < 0.00000001) )
    {
        throw divide_by_zero("Divide by zero...");//抛出一个异常类
    }
    
    return  a / b;
}

int main(int argc, char *argv[])
{
    try
    {
        cout<<Div(1, 0)<<endl;
    }
    catch(exception& e)//用引用,不发生拷贝
    //exception是父类,根据面向对象复制兼容性原则,
    //这里可以不用改成catch(divide_by_zero& e)
    //如果异常在异常类族里面,那么通过这个catch,就可以捕获所有异常
    {
        cout<<e.what()<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
result:
Divide by zero...
Press the enter key to continue ...

函数级try语法

可以将函数体作为一个完整的try语句块

函数级try语法可以更好将正常逻辑代码异常处理代码分开,提高代码的可读性与维护性。

#include <cstdlib>
#include <iostream>
#include <stdexcept>

using namespace std;

int func1(int i)//花括号太多,等价于下边的
{
    try
    {
        if( i > 0 )
        {
            return i;
        }
        else
        {
            throw "error";
        }
    }
    catch(...)
    {
        return -1;
    }
}

int func2(int i) try//这个函数是会产生异常的,产生的异常由后边的catch去捕获
{
    if( i > 0 )
    {
        return i;
    }
    else
    {
        throw "error";
    }
}
catch(...)
{
    return -1;
}

int main(int argc, char *argv[])
{
    for(int i=0; i<5; i++)
    {
        cout<<func2(i)<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
result:
-1
1
2
3
4
Press the enter key to continue ...

小结

catch(…)可以捕获所有异常

catch(…)经常作为最后一个catch语句出现

不要在构造函数中抛出异常,这样可能造成资源泄露

工程中经常以标准库中的异常类作为项目异常的基础

函数级try语句块能够更好的提高代码的维护性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值