zoukankan      html  css  js  c++  java
  • C++中的异常处理(下)

    1,catch 语句块中可以抛出异常:

        1,示意图:

     

        2,func() 在 try 语句块中,说明它有可能抛出异常,抛出的异常有可能是整型或其它类型;

        3,catch 语句块处理方式是将异常重新抛出去,其它什么也不干;

        4,此时需要外层的其它 try ... catch 语句块处理;

        5,catch() 中的参数类似于函数里面的参数,当 try() 里面抛出的异常在逐个匹配时,匹配上了以后异常元素就用来初始化 catch() 中的形参变量,因此 catch() 中的参数才会代表扔出来的异常,其实类似于函数调用过程有一个初始化的工作;

        6,函数调用里面实参和形参有可能进行类型转换,但是在 try ... catch 异常语句中绝对不会有任何的类型转换,严格匹配, 然后初始化;

        7,接受异常是任意类型时,只能通过 throw 扔出异常;

       

    2,为什么要在 catch 中重新抛出异常?

     

    3,catch 中捕获的异常可以被重新解释后抛出,工程中使用这样的统一异常类型:

        1,示意图:

     

        2,工程开发中使用 catch 中可以再次扔出异常的特性来重新解释一个异常;

        3,工程开发中一般会基于已有的库来进行,比如 STL 标准库,也有可能是我们自己的私有库;

        4,当我们发现私有库中有一些功能没有,但需要的功能在第三方库中是有的,所以我们要进行一层封装;

        5,在私有库中定义一个 MyFunc(int i) 函数,这个函数用来直接调用第三方库中的 func() 函数;

        6,进行封装的原因是 func() 函数的异常类型为 int 类型,比较简单,然而在私有库中我们定义了自己的异常类型为 Exception, 我们不想使用 int 类型,我们想要统一异常的类型,于是我们就在私有库中的 MyFunc() 函数中去捕获第三方库中 func() 函数抛出的异常,然后根据捕获的异常重新解释为我们想要的异常,这样我们工程开发中所面对的异常类型就是一致的;

        7,这就是工程中利用 catch 中可以重新抛出异常的特性来统一异常的类型;

       

    4,异常的重新解释编程实验:

      1 #include <iostream>
      2 #include <string>
      3 
      4 using namespace std;
      5 
      6 void Demo()
      7 {
      8     try
      9     {
     10         try
     11         {
     12             throw 'c';
     13         }
     14         catch(int i)
     15         {
     16             cout << "Inner: catch(int i)" << endl;
     17             throw i;
     18         }
     19         catch(...)
     20         {
     21             cout << "Inner: catch(...)" << endl;
     22             throw;
     23         }
     24     }
     25     catch(...)
     26     {
     27         cout << "Outer: catch(...)" << endl;
     28     }
     29 }
     30 
     31 /*
     32     假设: 当前的函数是第三方库中的函数,因此,我们无法修改源代码
     33     
     34     函数名: void func(int i)
     35     抛出异常的类型: int
     36                         -1 ==》 参数异常
     37                         -2 ==》 运行异常
     38                         -3 ==》 超时异常
     39 */
     40 
     41 /* 第三方库函数,编写好后不能修改,因为我们一般没有源代码,这里仅是用于模拟 */
     42 void func(int i)
     43 {
     44     if( i < 0 )
     45     {
     46         throw -1;
     47     }
     48     
     49     if( i > 100 )
     50     {
     51         throw -2;
     52     }
     53     
     54     if( i == 11 )
     55     {
     56         throw -3;
     57     }
     58     
     59     cout << "Run func..." << endl;  // 没有异常,则执行 func() 函数功能;
     60 }
     61 
     62 /* 不能修改 func() 函数,则我们定义 MyFunc() 函数重解释 func() 的异常 */
     63 void MyFunc(int i)
     64 {
     65     try
     66     {
     67         func(i);  // 直接通过第三方库中的 func() 来实现我们需要的功能;
     68     }
     69     catch(int i)
     70     {
     71         switch(i)
     72         {
     73             case -1:
     74                 throw "Invalid Parameter";
     75                 break;
     76             case -2:
     77                 throw "Runtime Exception";
     78                 break;
     79             case -3:
     80                 throw "Timeout Exception";
     81                 break;
     82         }
     83     }
     84 }
     85 
     86 int main(int argc, char *argv[])
     87 {
     88     // Demo();
     89     
     90     try
     91     {
     92         MyFunc(11);  // 如果在这里调用 func(11),则下面 catch 语句块中会打印:Exception Info: -3; 这样就不能立刻反应出来出了什么事儿,此时要去找 func() 中的文档说明,这样的开发是非常痛苦的;
     93     }
     94     catch(const char* cs)
     95     {
     96         cout << "Exception Info: " << cs << endl;  // 打印:Exception Info: Timeout Exception
     97     }
     98     
     99     return 0;
    100 }

        1,本例展示了工程中利用 catch 中可以重新抛出异常的特性来统一异常的类型;

        2,解释异常,重新抛出新的意义更加丰富的异常,字符串是不够的,可以定义自己的异常类类型,

       

    5,异常的操作特性:

        1,异常的类型可以是自定义类类型;

        2,对于类类型异常的匹配依旧是自上而下严格匹配;

           1,只有赋值兼容性是意外;

        3,赋值兼容性原则在异常匹配中依然适用;

           1,子类的异常对象可以被父类的 catch 语句块抓住;

        4,一般而言(这里是将异常类型编程自定义的类类型应该遵守的原则):

           1,匹配子类异常的 catch 放在下部;

           2,匹配父类异常的 catch 放在上部;

          

    6,工程中异常类的特性:

        1,在工程中会定义一系列的异常类;

           1,这些类是一个类族,有很严格的继承层次结构;

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

        3,代码复用时可能需要重解释不同的异常类;

           1,刚才展示的 catch 的特性;

        4,在定义 catch 语句块时推荐使用引用作为参数;

           1,catch 语句块要捕获的异常是类对象异常的时候,推荐使用引用作为参数,避免拷贝构造,提高程序效率;

       

    7,类型的异常编程实验:

      1 #include <iostream>
      2 #include <string>
      3 
      4 using namespace std;
      5 
      6 class Base
      7 {
      8 };
      9 
     10 /* 定义异常类 */
     11 class Exception : public Base
     12 {
     13     int m_id;
     14     string m_desc;
     15 public:
     16     Exception(int id, string desc)
     17     {
     18         m_id = id;
     19         m_desc = desc;
     20     }
     21     
     22     int id() const
     23     {
     24         return m_id;
     25     }
     26     
     27     string description() const
     28     {
     29         return m_desc;
     30     }
     31 };
     32 
     33 /*
     34     假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码
     35     函数名: void func(int i)
     36     抛出异常的类型: int
     37                         -1 ==》 参数异常
     38                         -2 ==》 运行异常
     39                         -3 ==》 超时异常
     40 */
     41 
     42 /* 第三方库函数,编写好后不能修改,因为我们一般没有源代码,这里仅是用于模拟 */
     43 void func(int i)
     44 {
     45     if( i < 0 )
     46     {
     47         throw -1;
     48     }
     49     
     50     if( i > 100 )
     51     {
     52         throw -2;
     53     }
     54     
     55     if( i == 11 )
     56     {
     57         throw -3;
     58     }
     59     
     60     cout << "Run func..." << endl;
     61 }
     62 
     63 /* 使用自定义的类类型来优化 */
     64 void MyFunc(int i)
     65 {
     66     try
     67     {
     68         func(i);
     69     }
     70     catch(int i)
     71     {
     72         switch(i)
     73         {
     74             case -1:
     75                 throw Exception(-1, "Invalid Parameter");  // 直接调用构造函数生成异常对象;
     76                 break;
     77             case -2:
     78                 throw Exception(-2, "Runtime Exception");  // 直接调用构造函数生成异常对象;
     79                 break;
     80             case -3:
     81                 throw Exception(-3, "Timeout Exception");  // 直接调用构造函数生成异常对象;
     82                 break;
     83         }
     84     }
     85 }
     86 
     87 int main(int argc, char *argv[])
     88 {
     89     try
     90     {
     91         MyFunc(11);
     92     }
     93     catch(const Exception& e)  // 这里使用异常类,为了防止拷贝构造、提高程序效率,用引用,同时也为了防止无休止递归调用导致栈溢出;
     94     {
     95         cout << "Exception Info: " << endl;  // Exception Info:
     96         cout << "   ID: " << e.id() << endl;  // ID: -3;
     97         cout << "   Description: " << e.description() << endl;  // Description: Timeout Exception;
     98     }
     99     catch(const Base& e)  // 如果把父类放到子类上面,则编译器显示:
    100                           // warning: exception of type 'Exception' will be caught(子类这一行)
    101                           // warning: by earlier handler for 'Base' (父类这一行);
    102     {
    103         cout << "catch(const Base& e)" << endl;
    104     }
    105     
    106     return 0;
    107 }

        1,异常信息更加丰富,更加方便的定位问题所在;

        2,工程开发中,一般以自定义类类型来描述可能出现的异常;

        3,工程开发中,子类上,父类下;

        4,本例告诉大家实际工程开发中定义完异常类的层次结构之后,如何来进行使用;

       

    8,C++ 标准库中的异常类:

        1,C++ 标准库中提供了实用异常类族;

        2,标准库中的异常都是从 exception 类派生的;

        3,exception 类有两个主要的分支:

           1,logic_error:

               1,常用于程序中的可避免逻辑错误;

                  1,空指针,函数参数错误,下标越界等;

           2,runtime_error:

               1,常用于程序中无法避免的恶心错误;

                  1,运算产生越界、溢出等;

        4,标准库中的异常类继承图:

     

           1,可以查看 C++ 标准库文档,看异常怎么使用;

          

    9,标准库中的异常使用编程实验(用异常类优化数组类):

        1,Array.h 的优化:

     1 #ifndef _ARRAY_H_
     2 #define _ARRAY_H_
     3 
     4 #include <stdexcept>  // 标准库中的异常类头文件;
     5 
     6 using namespace std;
     7 
     8 template
     9 < typename T, int N >
    10 class Array
    11 {
    12     T m_array[N];
    13 public:
    14     int length() const;
    15     bool set(int index, T value);
    16     bool get(int index, T& value);
    17     T& operator[] (int index);
    18     T operator[] (int index) const;
    19     virtual ~Array();
    20 };
    21 
    22 template
    23 < typename T, int N >
    24 int Array<T, N>::length() const
    25 {
    26     return N;
    27 }
    28 
    29 template
    30 < typename T, int N >
    31 bool Array<T, N>::set(int index, T value)
    32 {
    33     bool ret = (0 <= index) && (index < N);
    34     
    35     if( ret )
    36     {
    37         m_array[index] = value;
    38     }
    39     
    40     return ret;
    41 }
    42 
    43 template
    44 < typename T, int N >
    45 bool Array<T, N>::get(int index, T& value)
    46 {
    47     bool ret = (0 <= index) && (index < N);
    48     
    49     if( ret )
    50     {
    51         value = m_array[index];
    52     }
    53     
    54     return ret;
    55 }
    56 
    57 template
    58 < typename T, int N >
    59 T& Array<T, N>::operator[] (int index)
    60 {
    61     if( (0 <= index) && (index < N) )
    62     {
    63         return m_array[index];  // 这里之前没有验证 index 是否合法,因为验证了也没办法处理;
    64     }
    65     else
    66     {
    67         throw out_of_range("T& Array<T, N>::operator[] (int index)");
    68     }
    69 }
    70 
    71 template
    72 < typename T, int N >
    73 T Array<T, N>::operator[] (int index) const
    74 {
    75     if( (0 <= index) && (index < N) )
    76     {
    77         return m_array[index];  // 这里之前没有验证 index 是否合法,因为验证了也没办法处理;
    78     }
    79     else
    80     {
    81         throw out_of_range("T Array<T, N>::operator[] (int index) const");
    82     }
    83 }
    84 
    85 template
    86 < typename T, int N >
    87 Array<T, N>::~Array()
    88 {
    89 
    90 }
    91 
    92 #endif

        2,HeapArray.h 的优化:

      1 #ifndef _HEAPARRAY_H_
      2 #define _HEAPARRAY_H_
      3 
      4 #include <stdexcept>  // 添加标准头文件;
      5 
      6 using namespace std;
      7 
      8 template
      9 < typename T >
     10 class HeapArray
     11 {
     12 private:
     13     int m_length;
     14     T* m_pointer;
     15     
     16     HeapArray(int len);
     17     HeapArray(const HeapArray<T>& obj);
     18     bool construct();
     19 public:
     20     static HeapArray<T>* NewInstance(int length); 
     21     int length() const;
     22     bool get(int index, T& value);
     23     bool set(int index ,T value);
     24     T& operator [] (int index);
     25     T operator [] (int index) const;
     26     HeapArray<T>& self();
     27     const HeapArray<T>& self() const;  // 要考虑成员函数有没有必要成为 const 函数,const 函数主要是给 cosnt 函数调用;
     28     ~HeapArray();
     29 };
     30 
     31 template
     32 < typename T >
     33 HeapArray<T>::HeapArray(int len)
     34 {
     35     m_length = len;
     36 }
     37 
     38 template
     39 < typename T >
     40 bool HeapArray<T>::construct()
     41 {   
     42     m_pointer = new T[m_length];
     43     
     44     return m_pointer != NULL;
     45 }
     46 
     47 template
     48 < typename T >
     49 HeapArray<T>* HeapArray<T>::NewInstance(int length) 
     50 {
     51     HeapArray<T>* ret = new HeapArray<T>(length);
     52     
     53     if( !(ret && ret->construct()) ) 
     54     {
     55         delete ret;
     56         ret = 0;
     57     }
     58         
     59     return ret;
     60 }
     61 
     62 template
     63 < typename T >
     64 int HeapArray<T>::length() const
     65 {
     66     return m_length;
     67 }
     68 
     69 template
     70 < typename T >
     71 bool HeapArray<T>::get(int index, T& value)
     72 {
     73     bool ret = (0 <= index) && (index < length());
     74     
     75     if( ret )
     76     {
     77         value = m_pointer[index];
     78     }
     79     
     80     return ret;
     81 }
     82 
     83 template
     84 < typename T >
     85 bool HeapArray<T>::set(int index, T value)
     86 {
     87     bool ret = (0 <= index) && (index < length());
     88     
     89     if( ret )
     90     {
     91         m_pointer[index] = value;
     92     }
     93     
     94     return ret;
     95 }
     96 
     97 template
     98 < typename T >
     99 T& HeapArray<T>::operator [] (int index)
    100 {
    101     if( (0 <= index) && (index < length()) )
    102     {
    103         return m_pointer[index];  // 优化这里,越界抛异常;
    104     }
    105     else
    106     {
    107         throw out_of_range("T& HeapArray<T>::operator [] (int index)");
    108     }
    109 }
    110 
    111 template
    112 < typename T >
    113 T HeapArray<T>::operator [] (int index) const
    114 {
    115     if( (0 <= index) && (index < length()) )
    116     {
    117         return m_pointer[index];  // 优化这里,越界抛异常;
    118     }
    119     else
    120     {
    121         throw out_of_range("T HeapArray<T>::operator [] (int index) const");
    122     }
    123 }
    124 
    125 template
    126 < typename T >
    127 HeapArray<T>& HeapArray<T>::self()
    128 {
    129     return *this;
    130 }
    131 
    132 template
    133 < typename T >
    134 const HeapArray<T>& HeapArray<T>::self() const
    135 {
    136     return *this;
    137 }
    138 
    139 template
    140 < typename T >
    141 HeapArray<T>::~HeapArray()
    142 {
    143     delete[]m_pointer;
    144 }
    145 
    146 #endif

        3,使用:

     1 #include <iostream>
     2 #include <string>
     3 #include "Array.h"
     4 #include "HeapArray.h"
     5 
     6 using namespace std;
     7 
     8 void TestArray()
     9 {
    10     Array<int, 5> a;
    11     
    12     for(int i=0; i<a.length(); i++)
    13     {
    14         a[i] = i;  // 如果访问越界,则编译器显示: terminate called after throwing an instance of 'std::out_of_range' what():  T& Array<T, N>::operator[] (int index);已放弃
    15     }
    16         
    17     for(int i=0; i<a.length(); i++)
    18     {
    19         cout << a[i] << endl;
    20     }
    21 }
    22 
    23 void TestHeapArray()
    24 {
    25     HeapArray<double>* pa = HeapArray<double>::NewInstance(5);
    26     
    27     if( pa != NULL )
    28     {
    29         HeapArray<double>& array = pa->self();
    30         
    31         for(int i=0; i<array.length(); i++)
    32         {
    33             array[i] = i;
    34         }
    35             
    36         for(int i=0; i<array.length(); i++)
    37         {
    38             cout << array[i] << endl;
    39         }
    40     }
    41     
    42     delete pa;
    43 }
    44 
    45 int main(int argc, char *argv[])
    46 {
    47     
    48     try
    49     {
    50         TestArray();
    51         
    52         cout << endl;
    53         
    54         TestHeapArray();
    55     }
    56     catch(...)
    57     {
    58         cout << "Exception" << endl;
    59     }
    60     
    61     return 0;
    62 }

        1,在以后开发需要使用标准库的时候,要有当前开发所使用的函数或类会不会出现异常这个意识,这个时候需要查标准库的文档,看看每个函数的说明,每个类的说明;

       

    10,小结:

        1,catch 语句块中可以抛出异常;

        2,异常的类型可以是自定义类类型;

        3,赋值兼容性原则在异常匹配中依然适用;

        4,标准库中的异常都是从 exception 类派生的;

  • 相关阅读:
    struts2文件上传报错
    简述算法和程序的区别并举例说明
    JAVA中TreeMap集合筛选字母及每一个字符出现的次数
    Myeclipse2014破解步骤
    修改ubuntu的终端提示符
    gcc 引用math.h头文件,编译出现undefined reference to `pow‘等错误时,需要加参数lm.
    几篇文章
    gdb调试gcc出现:Missing separate debuginfos, use: debuginfoinstall glibcx.i686
    【达内C++学习培训学习笔记系列】C语言之三循环语句和数组
    code::block之spell checker配置
  • 原文地址:https://www.cnblogs.com/dishengAndziyu/p/10919877.html
Copyright © 2011-2022 走看看