38、不一样的C++系列--C++的异常处理

来源:互联网 发布:宜人贷淘宝验证不了 编辑:程序博客网 时间:2024/06/07 10:04

C++的异常处理

异常处理介绍

C++内置了异常处理的语法元素 try … catch …

  • try语句处理正常代码逻辑
  • catch语句处理异常情况
  • try语句中的异常由对应的catch语句处理
  • 语法:
try{    double r = divide(1, 0);}catch(...){    cout << "Divided by zero..." << endl;}
  • C++通过throw语句抛出异常信息
double divide(double a, double b){    const double delta = 0.0000000000001;    double ret = 0;    if(!((-delta < b) && (b < delta)))    {        ret = a / b;    }    else    {        //产生除0异常        throw 0;    }    return ret;}

异常处理分析

  • 这里对C++异常处理分析一下:
    • throw抛出的异常必须被catch处理
      • 当前函数能够处理异常,程序继续往下执行
      • 当前函数无法处理异常,则函数停止执行,并返回

未被处理的异常会顺着函数调用栈向上传播,知道被处理为止,否则程序将停止执行。

function1 ==> function2 ==> function3          <==           <== throw1;

这里举一个例子:

#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抛出异常信息        throw 0;    }    return ret;}int main(int argc, char *argv[]){        try    {        double r = divide(1, 0);        cout << "r = " << r << endl;    }    catch(...)    {        //try语句中抛出的异常在这里接受并处理        cout << "Divided by zero..." << endl;    }    return 0;}

继续学习 try… catch …的知识点:

  • 同一个try 语句可以跟上多个catch语句

    • catch语句可以定义具体处理的异常类型
    • 不同类型的异常由不同的catch语句负责处理
    • try语句中可以抛出任何类型的异常
    • catch( … ) 用于处理所有类型的异常
    • 任何异常都只能被捕获(catch)一次
  • 异常处理的匹配规则:

//异常处理匹配时,不进行任何的类型转换|   try|   {|       throw 1;|   }   |   catch(Type1 t1)|   {|   }|   catch(Type2 t2)|   {|   }|   catch(TypeN tn)|   {|   }v异常抛出后,自上而下严格匹配每一个catch语句处理的类型

这里用一个例子来试验一下匹配规则:

#include <iostream>#include <string>using namespace std;void Demo1(){    try    {           //这里抛出一个字符        throw 'c';    }    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(...)    {        cout << "catch(...)" << endl;    }}void Demo2(){    //这里抛出string类字符串    throw string("D.T.Software");}int main(int argc, char *argv[]){        Demo1();    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;    }    return 0;}

执行结果如下:

catch(char c)catch(string ss)

catch再抛出异常

在try … catch … 语句中,catch语句块中可以抛出异常

try{    func();}catch(int i){    //将捕获的异常重新抛出    throw i;}catch(...){    //将捕获的异常重新抛出    throw;}//catch中抛出的异常需要外层的try ... catch ...捕获

可是为什么要重新抛出异常呢?因为:

catch中捕获的异常可以被重新解释后抛出,这样就可以在工程开发中使用这样的方式统一异常类型。
//工程开发通过调用 MyFunc 获得 func函数的功能和统一的异常信息    |    | 调用    V//私有库void MyFunc(int i);/*异常类型为Exception*/    |    | 封装    V//第三方库void func(int i);/*异常类型为int*/

这里举一个例子:

#include <iostream>#include <string>using namespace std;void Demo(){    try    {        try        {            //这里抛出的异常由内层try ... catch ...来捕获处理            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(int argc, char *argv[]){    Demo();    try    {        MyFunc(11);    }    catch(const char* cs)    {        cout << "Exception Info: " << cs << endl;    }    return 0;}

运行结果为:

Inner: catch(...)Outer: catch(...)Exception Info: Timeout Exception

自定义异常类型

自定义类类型及匹配:

  • 异常的类型可以是自定义类类型
  • 对于类类型异常的匹配依旧是自上而下严格匹配
  • 赋值兼容性原则在异常匹配中依然适用
  • 一般而言
    • 匹配子类异常的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(int argc, char *argv[]){    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 Info:    ID: -3   Description: Timeout Exception

C++标准库的异常类族

在C++标准库中提供了实用异常类族

  • 标准库中的异常都是从 exception 类派生的
  • exception 类有两个主要的分支
    • logic_error
      • 常用于程序中的可避免逻辑错误
    • runtime_error
      • 常用于程序中无法避免的恶性错误

这里写图片描述

这里演示一下如何使用:

#include <iostream>#include <string>#include "Array.h"#include "HeapArray.h"using namespace std;void TestArray(){    Array<int, 5> a;    /*        这里如果越界会抛出异常:        throw out_of_range("T& Array<T, N>::operator[] (int index)");        throw out_of_range("T Array<T, N>::operator[] (int index) const");        */    for(int i=0; i<a.length(); i++)    {        a[i] = i;    }    for(int i=0; i<a.length(); i++)    {        cout << a[i] << endl;    }}void TestHeapArray(){    HeapArray<double>* pa = HeapArray<double>::NewInstance(5);    if( pa != NULL )    {        HeapArray<double>& array = pa->self();        /*        这里如果越界会抛出异常:        throw out_of_range("T& HeapArray<T>::operator [] (int index)");        throw out_of_range("T HeapArray<T>::operator [] (int index) const");        */        for(int i=0; i<array.length(); i++)        {            array[i] = i;        }        for(int i=0; i<array.length(); i++)        {            cout << array[i] << endl;        }    }    delete pa;}int main(int argc, char *argv[]){    try    {        TestArray();        cout << endl;        TestHeapArray();    }    catch(...)    {        cout << "Exception" << endl;    }    return 0;}

小结

  • C++中直接支持异常处理的概念
  • try … catch …是C++中异常处理的专用语句
  • try 语句处理正常代码逻辑,catch 语句处理异常情况
  • 同一个 try 语句可以跟上多个 catch 语句
  • 异常处理必须严格匹配,不进行任何的类型转换
  • catch语句块中可以抛出异常
  • 异常的类型可以是自定义类类型
  • 赋值兼容性原则在异常匹配中依然适用
  • 标准库中的异常都是从exception类派生的
原创粉丝点击