异常——C++中的异常

本文参照于狄泰软件学院——《C++深度剖析课程》

之前我们分析了C语言中异常处理的方式,基本没有好的解决方案。那么C++中会如何优化C语言对异常的处理的缺陷呢?

C++中的异常处理

  1. C++内置了异常处理的语法元素try…catch…
  2. try语句处理正常代码逻辑
  3. catch语句处理异常情况
  4. try语句中的异常由对应的catch语句处理
try
{
   double r = divide(1, 0);
}
catch
{
   cout << "Divided by zero..." << endl;
}

疑问:这种方式跟C语言的if…else…处理方式有什么区别吗?

  1. C++通过throw语句抛出异常信息
  2. throw抛出的异常必须被catch处理
    • 当前函数能够处理异常,程序继续往下执行
    • 当前函数无法处理异常,则函数停止执行,并返回。
  3. 未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。

这里写图片描述

示例代码:C++异常处理初探

#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(int argc, char *argv[])
{   
    try
    {
        double r = divide(1, 0);

        cout << "r = " << r << endl;
    }
    catch(...)  
    {
        cout << "Divided by zero..." << endl;
    }

    return 0;
}

分析:
1. 相对于C语言的处理方式,C++异常的处理方式更加直观了。
a) 不需要增加函数参数
b) 不需要返回异常信息
c) 不需要使用全局变量

  1. catch(…)用于处理所有类型的异常。

问题:C语言可以处理多个异常情况,那么C++的try…catch有这样的处理方式吗?

处理多个异常

示例代码:C++异常处理规则

#include <iostream>
#include <string>

using namespace std;

void Demo1()
{
    throw 3.14;

    try
    {   
        throw 'c';
    }
    catch(char c)
    {
        cout << "catch(char c)" << endl;
    }
    catch(short c)
    {
        cout << "catch(short c)" << endl;
    }    
}

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

int main(int argc, char *argv[])
{   
    try
    {
        Demo1();
    }
    catch(double d)
    {
        cout << "catch(double d)" << endl;
    } 

    try
    {
        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;
    }
    catch(...)
    {
        cout << "catch(...)" << endl;
    }
 }

输出结果:
catch(double d)
catch(string ss)

分析:
1、 当函数抛出(throw)异常时,该函数结束执行,相当于C语言中的return。
2、 当抛出的异常找不到相应的catche时,未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。

这就是Demo1() 一进入函数就抛出异常throw 3.14;马上结束了该函数的执行,返回到main函数中寻找相应的catch。如果没有相应的catch,程序就会被终止。

3、 catch 语句可以定义具体处理的异常类型
4、 不同类型的异常由不同的catch 语句负责处理,并且任何异常都只能被捕获(catch )一次
5、 try 语句中可以抛出任何类型的异常
6、 catch (…)用于处理所有类型的异常

这就是Demo2() ,抛出string类型的异常。在主函数中只会找到相应的catch(string ss)进行执行。对于后面的处理任何异常的catch(…)视而不见。

异常抛出后,至上而下严格匹配每一个catche语句处理的类型,不进行任何的类型转换。并且只会执行一次。如果找不到相应的catch。则顺着函数调用栈向上传播,知道被处理为止,否则程序将停止执行

在catch中抛异常使其与第三方库兼容

问题:如果我们在catch语句块中抛出异常。那么会怎么样?为什么需要这么做?

从上面我们的分析其实很容易知道,当throw时,就会停止执行该函数,相当于C语言中的return。
那么,在catch中抛出的异常必然需要在外层的try…catch…中捕获。
但是,我们关心的是:为什么要在catch中抛出异常,为什么不直接处理,这样不是多此一举吗?

原因是,在实际工程开发中,我们常常会使用第三方的库。而我们也有自己的私有库。
当然我们编写的代码风格及编码要求必须按照我们自己规定的编写。

但对于我们使用者来说,第三方库是不可见的,也就是不可修改的。
那么当我们代码风格和编码要求与第三方库相异时,也就是第三方库抛出的异常类型不适合我们时,我们就需要多加一层try…catch…进行对第三方库的封装,重新抛出异常,而这个异常的类型就能符合我们自己的编码要求了。

这里写图片描述

示例代码:异常的重新解释

/*
    假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码

    函数名: 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(int argc, char *argv[])
{
    // Demo();

    try
    {
        MyFunc(11);
    }
    catch(const char* cs)
    {
        cout << "Exception Info: " << cs << endl;
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/small_prince_/article/details/80533195
今日推荐