码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2018-06-04 11:47:38      阅读:247      评论:0      收藏:0      [点我收藏+]

标签:C++ 异常处理   try ... catch ...   异常类类型   exception 类   

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

技术分享图片

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

#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;
}

        我们来看看编译结果

技术分享图片

        我们再来试试 1/1 呢

技术分享图片

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

技术分享图片

         下来我们还是以代码为例来进行说明

#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;
}

        我们来看看会打印出什么

技术分享图片

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

技术分享图片

        因为字符串是字面量,所以它会匹配到 const char* cs 上,如果我们在 Demo2 函数中抛出的是 string("D.T.Software");看看会打印出什么

技术分享图片

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

技术分享图片

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

技术分享图片

        那么我们还是以代码为例来进行讲解

#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(...)。我们来看看结果

技术分享图片

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

技术分享图片

        输出结果一目了然,直接看到是超时异常。那么我们便直接定位到了问题,这样效率便会提高。在 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 用来获取他们的信息,我们来看看编译结果

技术分享图片

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

技术分享图片

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

技术分享图片

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


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083

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

标签:C++ 异常处理   try ... catch ...   异常类类型   exception 类   

原文地址:http://blog.51cto.com/12810168/2124396

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!