C++ 中的异常处理(五十三)

        咱们在上节博客中讲了 C 语言中的异常处理,今天咱们就来说下 C++ 中的异常处理。在 C++ 中内置异常处理的语法元素  try ... catch ...try 语句处理正常代码逻辑,catch 语句处理异常状况,try 语句中的异常由相对应的 catch 语句处理。C++ 经过 throw 语句抛出异常信息,throw 抛出的异常必须被 catch 处理,当前函数可以处理异常,程序继续往下执行;当前函数没法处理异常,则函数中止执行并返回。未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,不然程序将中止执行。以下ios

图片.png

        下来咱们就以代码为例来进行分析ide

#include <iostream>
#include <string>

using namespace std;

double divide(double a, double b)
{
    const double delta = 0.000000000000001;
    double ret = 0;
    
    if( !((-delta < b) && (b < delta)) )
    {
        ret = a / b;
    }
    else
    {
        throw 0;
    }
    
    return ret;
}

int main()
{
    try
    {
        double r = divide(1, 0);
        cout << "r = " << r << endl;
    }
    catch(...)
    {
        cout << "Divided by zero..." << endl;
    }

    return 0;
}

        咱们来看看编译结果函数

图片.png

        咱们再来试试 1/1 呢学习

图片.png

        已经正确实现了哈。C++ 的这个异常处理是否是很方便呢。同一个 try 语句是能够跟上多个 catch 语句的。catch 语句能够定义具体处理的异常类型,不一样类型的异常由不一样的 catch 语句负责处理;try 语句中能够抛出任何类型的异常,catch(...) 用于处理全部类型的异常,任何异常都只能被捕获(catch)一次。下来咱们来看看异常处理的匹配规则,以下spa

图片.png

         下来咱们仍是以代码为例来进行说明对象

#include <iostream>
#include <string>

using namespace std;

void Demo1()
{
    try
    {
        throw 0;
    }
    catch(char c)
    {
        cout << "catch(char c)" << endl;
    }
    catch(short c)
    {
        cout << "catch(short c)" << endl;
    }
    catch(double c)
    {
        cout << "catch(double c)" << endl;
    }
    catch(int c)
    {
        cout << "catch(int c)" << endl;
    }
}

void Demo2()
{
    throw "D.T.Software";
}

int main()
{
    try
    {
        Demo1();
        //Demo2();
    }
    catch(char* s)
    {
        cout << "catch(char* s)" << endl;
    }
    catch(const char* cs)
    {
        cout << "catch(const char* cs)" << endl;
    }
    catch(string ss)
    {
        cout << "catch(string ss)" << endl;
    }
    
    return 0;
}

        咱们来看看会打印出什么图片

图片.png

        咱们看到直接在最后匹配到了 int,由于抛出的 0 默认类型为 int,它不会进行默认类型的转换。咱们再来看看 Demo2 会打印出什么ip

图片.png

        由于字符串是字面量,因此它会匹配到 const char* cs 上,若是咱们在 Demo2 函数中抛出的是 string("D.T.Software");看看会打印出什么开发

图片.png

        便会打印出字符串了。那么在 catch语句中咱们还能够抛出异常,以下字符串

图片.png

        那么咱们为何要在 catch 语句中从新抛出异常呢?catch 中捕获的异常能够被从新解释后抛出,在工程开发中使用这样的方式统一异常类型,以下

图片.png

        那么咱们仍是以代码为例来进行讲解

#include <iostream>
#include <string>

using namespace std;

void Demo()
{
    try
    {
        try
        {
            throw 'c';
        }
        catch(int i)
        {
            cout << "Inner: catch(int i)" << endl;
            throw i;
        }
        catch(...)
        {
            cout << "Inner: catch(...)" << endl;
            throw;
        }
    }
    catch(...)
    {
        cout << "Outer: catch(...)" << endl;
    }
}

/*
    假设: 当前的函数式第三方库中的函数,所以,咱们没法修改源代码
    
    函数名: void func(int i)
    抛出异常的类型: int
                        -1 ==> 参数异常
                        -2 ==> 运行异常
                        -3 ==> 超时异常
*/

void func(int i)
{
    if( i < 0 )
    {
        throw -1;
    }
    
    if( i > 100 )
    {
        throw -2;
    }
    
    if( i == 11 )
    {
        throw -3;
    }
    
    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }
    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw "Invalid Parameter";
                break;
            case -2:
                throw "Runtime Exception";
                break;
            case -3:
                throw "Timeout Exception";
                break;
        }
    }
}

int main()
{
    Demo();
/*    
    try
    {
        MyFunc(11);
    }
    catch(const char* cs)
    {
        cout << "Exception Info: " << cs << endl;
    }
*/    
    return 0;
}

        咱们先以 Demo 函数为例来进行分析,在 try 语句中的 try 语句里抛出 c,匹配到 catch(...) 语句中,先打印出 Inner: catch(...),再次抛出。匹配到外面的 catch(...) 语句中,先打印出 Outer: catch(...)。咱们来看看结果

图片.png

        咱们看到和咱们的分析是彻底一致的。接下来的 func 函数就好比是第三方的源码,咱们得根据这个功能写个一个属于咱们本身的 func 函数。咱们改写完以后是否是就一目了然呢?好比没改写以前,抛出个 11 的异常,对应的便会打印出 -3,咱们还得去查这个 -3 表明啥意思。咱们来注释掉 Demo 函数,看看下面的编译结果

图片.png

        输出结果一目了然,直接看到是超时异常。那么咱们便直接定位到了问题,这样效率便会提升。在 C++ 中,异常的类型能够是自定义类类型,对于类类型异常的匹配依旧是至上而下严格匹配,赋值兼容性原则在异常匹配中依然适用。通常而言,将匹配子类异常的 catch 放在上部,匹配父类异常的 catch 放在下部。在工程中会定义一系列的异常类,每一个类表明工程中可能出现的一种异常类型。代码复用时可能须要解释不一样的异常类,在定义 catch 语句时须要推荐使用引用做为参数。

        接下来咱们仍是以代码为例来进行分析

#include <iostream>
#include <string>

using namespace std;

class Base
{
};

class Exception : public Base
{
    int m_id;
    string m_desc;
public:
    Exception(int id, string desc)
    {
        m_id = id;
        m_desc = desc;
    }
    
    int id() const
    {
        return m_id;
    }
    
    string description() const
    {
        return m_desc;
    }
};

/*
    假设: 当前的函数式第三方库中的函数,所以,咱们没法修改源代码
    
    函数名: void func(int i)
    抛出异常的类型: int
                        -1 ==> 参数异常
                        -2 ==> 运行异常
                        -3 ==> 超时异常
*/

void func(int i)
{
    if( i < 0 )
    {
        throw -1;
    }
    
    if( i > 100 )
    {
        throw -2;
    }
    
    if( i == 11 )
    {
        throw -3;
    }
    
    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }
    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw Exception(-1, "Invalid Parameter");
                break;
            case -2:
                throw Exception(-2, "Runtime Exception");
                break;
            case -3:
                throw Exception(-3, "Timeout Exception");
                break;
        }
    }
}

int main()
{
    try
    {
        MyFunc(11);
    }
    catch(const Exception& e)
    {
        cout << "Exception Info: " << endl;
        cout << "    ID: " << e.id() << endl;
        cout << "    Description: " << e.description() << endl;
    }
    catch(const Base& e)
    {
        cout << "catch(const Base& e)" << endl;
    }

    return 0;
}

        咱们看到定义了两个类,在类 Exception 中定义了 id 和 description 用来描述他们的信息,再在 MyFunc 函数中生成临时对象 Exception 用来获取他们的信息,咱们来看看编译结果

图片.png

        这样的信息是否是更加直观呢。若是咱们将上面的 catch 语句中的父类放在子类前面呢,看看结果

图片.png

        咱们看到编译已经警告了,运行后它打印的是父类的信息,由于它一样遵循赋值兼容性原则。咱们在以前说的,将匹配子类异常的 catch 放在上部,匹配父类异常的 catch 放在下部。必定要遵循这个规则。在 C++ 标准库中提供了实用异常类族,标准库中的异常都是从 exception 类派生的,exception 类有两个主要的分支:a> logic_error 经常使用于程序中可避免逻辑错误;b> runtime_error 经常使用于程序中没法避免的恶性错误。下图是标准库中的异常类关系

图片.png

        经过对异常的学习,总结以下:一、C++ 中直接支持异常处理的概念;二、try...catch..是 C++ 中异常处理的专用语句;三、try 语句处理正常代码逻辑,catch 语句处理异常状况,同一个 try 语句能够跟上多个 catch 语句;四、异常处理必须严格匹配,不进行任何的转换;五、catch 语句块中能够抛出异常,异常的类型能够是自定义类类型;六、赋值兼容性原则在异常匹配中依然适用;七、标准库中的异常都是从 exception 类派生的。


        欢迎你们一块儿来学习 C++ 语言,能够加我QQ:243343083

相关文章
相关标签/搜索