zoukankan      html  css  js  c++  java
  • c++中的异常处理

     目录

      1、 异常 与 Bug 的区别

      2、c++中的异常处理方式(try ... catch ...)

      3、自定义异常类的使用方式

      4、C++ 标准库中的异常类

      5、try..catch 另类写法 和  函数异常声明/定义 throw()

    1、 异常 与 Bug 的区别  

      “异常”是我们在程序开发中必须考虑的一些特殊情况,是程序运行时就可预料的执行分支(注:异常是不可避免的,如程序运行时产生除 0 的情况;打开的外部文件不存在;数组访问的越界等等);

      “Bug”是程序的缺陷,是程序运行时不被预期的运行方式(注:Bug是人为的、可避免的;如使用野指针;堆数组使用结束后未释放等等);

      无论是“异常”还是“Bug”,都是程序正常运行过程中可能出现的意外情况。区别是“异常”可以捕获,并做出合适的处理,“Bug"所带来的后果是无法预测的,需要重写相应的代码。

    2、c++中的异常处理方式(try ... catch ...)

     (1)基本语法

    1 // 异常基本语法
    2 try
    3 {
    4     // 可能产生异常的代码,若发生异常,通过 throw 关键字抛出异常
    5 }
    6 catch(异常类型) // 异常捕获,注(...)代表捕获所有异常
    7 {
    8     // 处理异常的代码,该异常由try语句块产生
    9 }

     (2)基本规则 

      1)同一个 try 语句可以跟上多个 catch 语句(在工程中,将可能产生异常的代码放在 try 语句块中,然后后面跟上多个 catch 语句);

      2)try 语句中通过 throw 关键字 可以抛出任何类型的异常(int、字符串、对象等等); 

      3)catch 语句可以定义具体处理的异常类型,如 catch( int ) 只捕获 int 类型异常, 但无法进一步获取异常信息;catch( int a ) 只捕获 int 类型异常,可以进一步获取异常信息;

      4)不同类型的异常由不同的 catch 语句负责处理;

      5)catch(...) 用于处理所有类型的异常(只能放在所有 catch语句 的后面);

      6)任何异常都只能被捕获(catch)一次;

      7)只要被 catch 捕获一次,其它的 catch 就没有捕获机会了;

      8)throw 抛出异常的类型 与 catch 捕获异常的类型  必须严格匹配(不能进行类型转换);若匹配成功,则能够捕获该异常;否则捕获失败,程序停止执行。

        

      其实,为了更好的理解 异常抛出 和 异常捕获 这两个动作,我们可以将其想象成 函数调用,抛出异常好比是函数中实参,捕获异常好比是函数中形参,只有当实参的类型 与 形参的类型严格匹配时,这次捕获才能成功。为什么说想象成函数调用,而不是正真的函数调用呢?原因就是:函数调用时,用实参初始化形参时可以进行隐式的类型转换;但是在异常捕获时,必须严格匹配异常类型。  

    (3)异常抛出(throw exception)的逻辑分析

      情况1:异常代码没有放到  try{ }  语句中,这也意味着没有对应的 catch 语句,其实就是普通函数调用,若此时抛出异常(throw),则程序停止执行;

      情况2:异常代码放到  try{ throw exception... }  语句中,这也意味着有对应的 catch 语句,则抛出异常时就会与 catch语句严格匹配;若匹配成功,则可以捕获该异常,否则不能捕获该异常,程序停止执行。

      throw 抛出异常后,在发生异常的函数中至上而下的严格匹配每一个 catch 语句捕获的异常类型,来判断是否能够捕获该异常;

       1) 若发生异常的函数中  能够捕获该异常,则程序接着往下执行;

       2) 若发生异常的函数中  不能捕获该异常,则未被处理的异常会顺着函数调用栈向上传播,直到该异常被捕获为止,否则程序将停止执行;

      总结:throw 抛出的异常必须被 对应的 catch 捕获,否则程序将停止执行;

        

      通过上图来说明异常抛出后的执行顺序:

      1)在 函数 function3 中 抛出异常 throw 1;但是在 function3 中并不能捕获该异常,则异常继续向外层函数 function2 抛出

      2)在 函数 function2 中,异常依旧没有被捕获,则异常继续向外层函数 function1 抛出;

      3)在 函数 function1 中,异常被 catch 捕获和处理,然后程序继续向下执行;

        注:若在 函数 function1 中,异常还是没有被捕获,则异常会一直向外层函数抛出,直到该异常被捕获为止,否在程序停止执行。 

      代码展示:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 double divide(double a, double b)
     7 {
     8     const double delta = 0.000000001;
     9     double ret = 0;
    10 
    11     if( !((-delta < b) && (b < delta)) )
    12     {
    13         ret = a / b;
    14     }
    15     else
    16     {
    17         throw "Divided by zero..."; 
    18     }
    19 
    20     cout << "divide(double a, double b)" << endl;
    21      
    22     return ret;
    23 }
    24 
    25 double ExceptionFunc()
    26 {
    27     double d = divide(2, 0);
    28     
    29     cout << "ExceptionFunc()" << endl;
    30     
    31     return d;
    32 }
    33 
    34 int main(int argc, char *argv[])
    35 {      
    36     double d = ExceptionFunc();
    37     
    38     cout << "result = " << d << endl;
    39 
    40     return 0;
    41 }
    42 
    43 /**
    44  * 运行结果:
    45  * terminate called after throwing an instance of 'char const*'
    46  * Aborted (core dumped)
    47  */
    48  
    49 /**
    50  * 分析:
    51  * throw "Divided by zero..."; 抛出异常后,divide(double a, double b) 函数不能捕获该异常,则异常继续抛给 ExceptionFunc();
    52  * 在 ExceptionFunc() 中,也不能捕获该异常,则异常继续抛给 main();
    53  * 在 main()中,也不能捕获该异常,则程序停止执行。
    54  */
    情况1 :异常案列复现

      

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 double divide(double a, double b)
     7 {
     8     const double delta = 0.000000001;
     9     double ret = 0;
    10     
    11     try
    12     {
    13         if( !((-delta < b) && (b < delta)) )
    14         {
    15             ret = a / b;
    16         }
    17         else
    18         {
    19             throw "Divided by zero..."; 
    20         }
    21     }
    22     catch(char const* s)  
    23     {
    24         cout << s << endl;
    25     } 
    26         
    27     cout << "divide(double a, double b)" << endl;
    28  
    29     return ret;
    30 }
    31 
    32 double ExceptionFunc()
    33 {   
    34     double d = divide(2, 0);
    35     
    36     cout << "ExceptionFunc()" << endl;
    37     
    38     return d;
    39 }
    40 
    41 int main(int argc, char *argv[])
    42 { 
    43     ExceptionFunc();
    44       
    45     cout << "test end!" << endl;
    46 
    47     return 0;
    48 }
    49 
    50 /**
    51  * 运行结果:
    52  * Divided by zero...
    53  * divide(double a, double b)
    54  * ExceptionFunc()
    55  * test end!
    56  */
    57  
    58 /**
    59  * 分析:
    60  * throw "Divided by zero..."; 抛出异常后,在divide(double a, double b) 中,异常被捕获,则程序继续向下执行;
    61  *  catch(char const* s)    // throw "Divided by zero..."
    62  *  {
    63  *     cout << s << endl;
    64  *  }   
    65  *  cout << "divide(double a, double b)" << endl;
    66  *
    67  *  divide(2, 0); 函数调用结束,返回到 ExceptionFunc() 中,
    68  *  cout << "ExceptionFunc()" << endl;  
    69  *   
    70  *  ExceptionFunc()调用结束,返回 main()中,继续向下执行;
    71  *  cout << "test end!" << endl;
    72  */
    情况2 :异常案列复现1
     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 double divide(double a, double b)
     7 {
     8     const double delta = 0.000000001;
     9     double ret = 0;
    10           
    11     if( !((-delta < b) && (b < delta)) )
    12     {
    13         ret = a / b;
    14     }
    15     else
    16     {
    17         throw "Divided by zero..."; 
    18     }
    19 
    20     cout << "divide(double a, double b)" << endl;
    21 
    22     return ret;
    23 }
    24 
    25 double ExceptionFunc()
    26 {
    27     double d;
    28     
    29     try
    30     {
    31         d = divide(2, 0);
    32             
    33         cout << "d = " << d << endl;
    34     }
    35     catch(char const* s)
    36     {
    37         cout << s << endl;
    38     }           
    39     
    40     cout << "ExceptionFunc()" << endl;
    41     
    42     return d;
    43 }
    44 
    45 int main(int argc, char *argv[])
    46 { 
    47     ExceptionFunc();
    48     
    49     cout << "test end!" << endl;
    50 
    51     return 0;
    52 }
    53 
    54 /**
    55  * 运行结果:
    56  * Divided by zero...
    57  * ExceptionFunc()
    58  * test end!
    59  */
    60  
    61 /**
    62  * 分析:
    63  * throw "Divided by zero..."; 抛出异常后,divide(double a, double b) 函数不能捕获该异常,则异常继续抛给 ExceptionFunc();
    64  * 在 ExceptionFunc() 中,异常被捕获,则程序继续向下执行;
    65  *  catch(char const* s)    // throw "Divided by zero..."
    66  *  {
    67  *     cout << s << endl;
    68  *  }   
    69  *  cout << "ExceptionFunc()" << endl;     
    70  *  ExceptionFunc()调用结束,返回 main()中,继续向下执行;
    71  *  cout << "test end!" << endl;
    72  */
    情况2 :异常案列复现2
     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 double divide(double a, double b)
     7 {
     8     const double delta = 0.000000001;
     9     double ret = 0;
    10 
    11     if( !((-delta < b) && (b < delta)) )
    12     {
    13         ret = a / b;
    14     }
    15     else
    16     {
    17         throw "Divided by zero..."; 
    18     }
    19  
    20     cout << "divide(double a, double b)" << endl;
    21 
    22     return ret;
    23 }
    24 
    25 double ExceptionFunc()
    26 {
    27     double d = divide(2, 0);
    28     
    29     cout << "ExceptionFunc()" << endl;
    30     
    31     return d;
    32 }
    33 
    34 int main(int argc, char *argv[])
    35 { 
    36     try
    37     {
    38         double d = ExceptionFunc();
    39             
    40         cout << "d = " << d << endl;
    41     }
    42     catch(char const* s)
    43     {
    44         cout << s << endl;
    45     }        
    46     
    47     cout << "test end!" << endl;
    48 
    49     return 0;
    50 }
    51 
    52 /**
    53  * 运行结果:
    54  * Divided by zero...
    55  * test end!
    56  */
    57  
    58 /**
    59  * 分析:
    60  * throw "Divided by zero..."; 抛出异常后,divide(double a, double b) 函数不能捕获该异常,则异常继续抛给 ExceptionFunc();
    61  * 在 ExceptionFunc() 中,也不能捕获该异常,则异常继续抛给 main();
    62  * 在 main()中,异常与被捕获,则程序继续向下执行
    63  *  catch(char const* s)    // throw "Divided by zero..."
    64  *  {
    65  *     cout << s << endl;
    66  *  }   
    67  *  cout << "test end!" << endl;     
    68  */
    情况2 :异常案列复现3
     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 void Demo1()
     7 {
     8     try
     9     {   
    10         throw 2.0;
    11     }
    12     catch(char c)
    13     {
    14         cout << "catch(char c)" << endl;
    15     }
    16     catch(short c)
    17     {
    18         cout << "catch(short c)" << endl;
    19     }
    20     catch(double c)   // throw 2.0;
    21     {
    22         cout << "catch(double c)" << endl;
    23     }
    24     catch(...)   // 表示捕获任意类型的异常
    25     {
    26         cout << "catch(...)" << endl;
    27     }   
    28 }
    29 
    30 void Demo2()
    31 {
    32     throw string("D.T.Software"); 
    33 }
    34 
    35 int main(int argc, char *argv[])
    36 {    
    37     Demo1();
    38     
    39     try
    40     {
    41         Demo2();      
    42     }
    43     catch(char* s)
    44     {
    45         cout << "catch(char *s)" << endl;
    46     }
    47     catch(const char* cs)
    48     {
    49         cout << "catch(const char *cs)" << endl;
    50     }
    51     catch(string ss)    // throw string("D.T.Software"); 
    52     {
    53         cout << "catch(string ss)" << endl;
    54     }  
    55     
    56     return 0;
    57 }
    58 /**
    59  * 运行结果:
    60  * catch(double c)
    61  * catch(string ss)
    62  */
    63 
    64 // 结论:异常类型严格匹配,(...)表示捕获任意类型的异常
    情况2 :多分支的异常捕获

       总结:

      情况1:只抛出异常,没有对应地异常捕获;(  没有 try ... catch ... 结构  )

      情况2:在try语句块中抛出异常(直接在try语句块中使用 throw 抛出异常;或者try语句块中放入有异常的函数,间接地在函数中使用 throw 抛出异常),然后通过catch语句捕获同类型的异常并进行异常处理;( 现象: 一个 try ... catch ... 结构 )

      那么现在我们考虑能不能在情况2的基础上,将捕获到异常继续抛出呢?(在 catch 语句块中重新抛出异常?)

      可以在 catch 语句中重新抛出异常,此时需要外层的 try ... catch ...捕获这个异常;

      注:catch 语句中只抛出异常,什么也不做;当捕获任意类型的异常时,直接使用 throw 就行。

      

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 void Demo()
     7 {
     8     try
     9     {
    10         throw 'c';
    11     }
    12     catch(int i)
    13     {
    14         cout << "Inner: catch(int i)" << endl;
    15         throw i;
    16     }
    17     catch(...)
    18     {
    19         cout << "Inner: catch(...)" << endl;
    20         throw;
    21     }
    22 }
    23 
    24 int main(int argc, char *argv[])
    25 {
    26     Demo();
    27     
    28     return 0;
    29 }
    30 
    31 /**
    32  * 运行结果:
    33  * Inner: catch(...)
    34  * terminate called after throwing an instance of 'char'
    35  * Aborted (core dumped)
    36  */
    37  // 错误原因:异常重解释之后,没有被捕获
    异常的重新解释案列(error)
     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 int main(int argc, char *argv[])
    32 {
    33     Demo();
    34     
    35     return 0;
    36 }
    37 /**
    38  * 运行结果:
    39  * Inner: catch(...)
    40  * Outer: catch(...)
    41  */
    异常的重新解释案列

       为什么要在 catch 语句块中重新抛出异常?

      在工程中,利用在 catch 语句块中重新解释异常并抛出这一特性,可以统一异常类型。(很晦涩,看下面解释....)

      

      我们通过上图来详细说明这个情况:

      背景介绍:出于开发效率考虑,在工程开发中一般会基于第三方库来开发,但是,第三方库中的某些功能并不完善(可读性差),此时就需要封装第三方库中的这个功能。

      在上图中,由于第三方库中 func()函数的异常类型是 int 类型,可读性很差,不能够直接通过异常结果知道该异常所代表的意思;基于这种情况,我们通过私有库中的 MyFunc()函数对第三方库中func()进行了封装,使得 MyFunc()函数中的异常类型可以显示更多的异常信息(MyFunc() 函数中的异常类型是自定义类型,可以是字符串、类类型等等),增强代码的可读性;为了将 func()中的异常类型 和 MyFunc() 中异常类型统一起来,我们可以在私有库中的MyFunc() 函数中去捕获第三方库中的 func() 函数抛出的异常,然后根据捕获到的异常重新解释为我们想要的异常,这样我们工程开发中所面对的异常类型就是一致的;接下来我们用代码复现这个场景:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 /*
     7     假设:当前的函数是第三方库中的函数,因此,我们无法修改源代码
     8     
     9     函数名: void func(int i)
    10     抛出异常的类型: int
    11                         -1 ==》 参数异常
    12                         -2 ==》 运行异常
    13                         -3 ==》 超时异常
    14 */
    15 void func(int i)
    16 {
    17     if( i < 0 )
    18     {
    19         throw -1;
    20     }
    21     
    22     if( i > 100 )
    23     {
    24         throw -2;
    25     }
    26     
    27     if( i == 11 )
    28     {
    29         throw -3;
    30     }
    31     
    32     cout << "Run func..." << endl;
    33 }
    34 
    35 int main(int argc, char *argv[])
    36 {
    37     try
    38     {
    39         func(11);
    40     }
    41     catch(int i)
    42     {
    43         cout << "Exception Info: " << i << endl;
    44     }
    45     
    46     return 0;
    47 }
    48 
    49 /**
    50  * 运行结果:
    51  * Exception Info: -3
    52  */
    53  
    54 // 异常显示结果太简单,当发生异常时,需要查询技术文档才能知道这儿的-3代表的意思,可读性很差
    模拟 第三方库异常代码案列

    // 异常的重解释,在 catch 语句中重新抛出异常

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 /*
     7     假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码
     8     
     9     函数名: void func(int i)
    10     抛出异常的类型: int
    11                         -1 ==》 参数异常
    12                         -2 ==》 运行异常
    13                         -3 ==》 超时异常
    14 */
    15 void func(int i)
    16 {
    17     if( i < 0 )
    18     {
    19         throw -1;
    20     }
    21     
    22     if( i > 100 )
    23     {
    24         throw -2;
    25     }
    26     
    27     if( i == 11 )
    28     {
    29         throw -3;
    30     }
    31     
    32     cout << "Run func..." << endl;
    33 }
    34 
    35 /* 不能修改 func() 函数,则我们定义 MyFunc() 函数重解释 func() 的异常 */
    36 void MyFunc(int i)
    37 {
    38     try
    39     {
    40         func(i);
    41     }
    42     catch(int i)
    43     {
    44         switch(i)
    45         {
    46             case -1:
    47                 throw "Invalid Parameter";   // 可以抛出更详细的数据,如类对象,后续讲解
    48                 break;
    49             case -2:
    50                 throw "Runtime Exception";
    51                 break;
    52             case -3:
    53                 throw "Timeout Exception";
    54                 break;
    55         }
    56     }
    57 }
    58 
    59 int main(int argc, char *argv[])
    60 {
    61     try
    62     {
    63         MyFunc(11);
    64     }
    65     catch(const char* cs)
    66     {
    67         cout << "Exception Info: " << cs << endl;
    68     }
    69     
    70     return 0;
    71 }
    72 /**
    73  * 运行结果:
    74  * Exception Info: Timeout Exception
    75  */
    模拟 第三方库异常代码(改进版)

     3、自定义异常类的使用方式

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

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

      (3)赋值兼容性原则在异常匹配中依然适用;(注:在赋值兼容性中,子类是特殊的父类,父类可以捕获子类的异常;同样满足异常类型严格匹配的原则)

        (4)在赋值兼容性原则中,一般将

        1)匹配子类异常的 catch 放在上部;

        2)匹配父类异常的 catch 放在下部;

      (5)在定义 catch 语句块时,推荐使用 const 引用作为参数,提高程序的运行效率;

      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 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;
     60 }
     61 
     62 /* 使用自定义的类类型来优化 */
     63 void MyFunc(int i)
     64 {
     65     try
     66     {
     67         func(i);
     68     }
     69     catch(int i)
     70     {
     71         switch(i)
     72         {
     73             case -1:
     74                 throw Exception(-1, "Invalid Parameter");  // 直接调用构造函数生成异常对象;
     75                 break;
     76             case -2:
     77                 throw Exception(-2, "Runtime Exception");
     78                 break;
     79             case -3:
     80                 throw Exception(-3, "Timeout Exception");
     81                 break;
     82         }
     83     }
     84 }
     85 
     86 int main(int argc, char *argv[])
     87 {
     88     try
     89     {
     90         MyFunc(11);
     91     }
     92     catch(const Exception& e)  // 1 使用 const 引用类型作为参数  2 赋值兼容性原则(参考第4点)
     93     {
     94         cout << "Exception Info: " << endl;
     95         cout << "   ID: " << e.id() << endl;
     96         cout << "   Description: " << e.description() << endl;
     97     }
     98     catch(const Base& e) 
     99     {
    100         cout << "catch(const Base& e)" << endl;
    101     }
    102     
    103     return 0;
    104 }
    105 /**
    106  * 运行结果:
    107  * Exception Info:
    108  * ID: -3
    109  * Description: Timeout Exception
    110  */
    模拟 第三方库异常代码(完美版)

    4、C++ 标准库中的异常类

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

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

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

        1)logic_error

             2)runtime_error

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

        

     1 #include <iostream>
     2 #include <string>
     3 #include <stdexcept> 
     4 #include <sstream>
     5 
     6 using namespace std;
     7 
     8 /*
     9     __FILE__  __FUNCTION__  const char[]
    10     __LINE__  int
    11 */
    12 
    13 template
    14 <typename T, int N>
    15 class Array
    16 {
    17 private:
    18     T arr[N];
    19 public:
    20     Array();
    21     T& operator[] (int index);
    22     void print() const;
    23 };
    24 
    25 template
    26 <typename T, int N>
    27 Array<T, N>::Array()
    28 {
    29     for(int i = 0; i < N; i++)
    30     {
    31         arr[i] = 0;
    32     }
    33 }
    34 
    35 template
    36 <typename T, int N>
    37 T& Array<T, N>::operator[] (int index)
    38 {
    39     if( (0 <= index) && (index < N) )
    40     {
    41         return arr[index];
    42     }
    43     else
    44     {
    45         ostringstream oss;
    46         
    47         throw out_of_range(string(__FILE__) + ":" + static_cast<ostringstream&>(oss << __LINE__).str() + ":" + __FUNCTION__
    48 
    49                         + "	 >> exception: the index of array is out of range");  
    50     }
    51 }
    52 
    53 template
    54 <typename T, int N>
    55 
    56 void Array<T, N>::print() const
    57 {
    58     for(int i = 0; i < N; i++)
    59     {
    60         cout << arr[i] << " ";
    61     }
    62     cout << endl;
    63 }
    64 
    65 int main(int argc, char *argv[])
    66 {
    67     try
    68     {
    69         Array<int, 5> arr;
    70 
    71         arr.print();
    72         arr[-1] = 1;    // 异常测试
    73         arr.print();
    74     }
    75 
    76     catch(const out_of_range& e)  // 1 使用 const 引用类型作为参数  2 赋值兼容性原则(参考第4点)
    77     {
    78         cout << e.what() << endl;
    79     }
    80 
    81     catch(...)
    82     {
    83         cout << "other exception ... " << endl;
    84     }
    85 
    86     return 0;
    87 }
    88 
    89 /**
    90  * 运行结果:
    91  * 0 0 0 0 0
    92  * test.cpp:47:operator[]     >> exception: the index of array is out of range
    93  */
    out_of_range异常类的使用
      1 // 模板文件 Array.hpp 的优化
      2 #ifndef ARRAY_H
      3 #define ARRAY_H
      4 
      5 #include <stdexcept>  // 标准库中的异常类头文件;
      6 
      7 using namespace std;A
      8 
      9 template
     10 < typename T, int N >
     11 class Array
     12 {
     13     T m_array[N];
     14 public:
     15     int length() const;
     16     bool set(int index, T value);
     17     bool get(int index, T& value);
     18     T& operator[] (int index);
     19     T operator[] (int index) const;
     20     virtual ~Array();
     21 };
     22 
     23 template
     24 < typename T, int N >
     25 int Array<T, N>::length() const
     26 {
     27     return N;
     28 }
     29 
     30 template
     31 < typename T, int N >
     32 bool Array<T, N>::set(int index, T value)
     33 {
     34     bool ret = (0 <= index) && (index < N);
     35     
     36     if( ret )
     37     {
     38         m_array[index] = value;
     39     }
     40     
     41     return ret;
     42 }
     43 
     44 template
     45 < typename T, int N >
     46 bool Array<T, N>::get(int index, T& value)
     47 {
     48     bool ret = (0 <= index) && (index < N);
     49     
     50     if( ret )
     51     {
     52         value = m_array[index];
     53     }
     54     
     55     return ret;
     56 }
     57 
     58 template
     59 < typename T, int N >
     60 T& Array<T, N>::operator[] (int index)
     61 {
     62     if( (0 <= index) && (index < N) )
     63     {
     64         return m_array[index];  // 这里之前没有验证 index 是否合法,因为验证了也没办法处理;
     65     }
     66     else
     67     {
     68         throw out_of_range("T& Array<T, N>::operator[] (int index)");
     69     }
     70 }
     71 
     72 template
     73 < typename T, int N >
     74 T Array<T, N>::operator[] (int index) const
     75 {
     76     if( (0 <= index) && (index < N) )
     77     {
     78         return m_array[index];  // 这里之前没有验证 index 是否合法,因为验证了也没办法处理;
     79     }
     80     else
     81     {
     82         throw out_of_range("T Array<T, N>::operator[] (int index) const");
     83     }
     84 }
     85 
     86 template
     87 < typename T, int N >
     88 Array<T, N>::~Array()
     89 {
     90 
     91 }
     92 
     93 #endif
     94 
     95 //------------------------------------------------------------------
     96 
     97 // 模板文件 HeapArray.hpp 的优化
     98 
     99 #ifndef HEAPARRAY_H
    100 #define HEAPARRAY_H
    101 
    102 #include <stdexcept>  // 添加标准头文件;
    103 
    104 using namespace std;
    105 
    106 template
    107 < typename T >
    108 class HeapArray
    109 {
    110 private:
    111     int m_length;
    112     T* m_pointer;
    113     
    114     HeapArray(int len);
    115     HeapArray(const HeapArray<T>& obj);
    116     bool construct();
    117 public:
    118     static HeapArray<T>* NewInstance(int length); 
    119     int length() const;
    120     bool get(int index, T& value);
    121     bool set(int index ,T value);
    122     T& operator [] (int index);
    123     T operator [] (int index) const;
    124     HeapArray<T>& self();
    125     const HeapArray<T>& self() const;  // 要考虑成员函数有没有必要成为 const 函数,const 函数主要是给 cosnt 对象调用;
    126     ~HeapArray();
    127 };
    128 
    129 template
    130 < typename T >
    131 HeapArray<T>::HeapArray(int len)
    132 {
    133     m_length = len;
    134 }
    135 
    136 template
    137 < typename T >
    138 bool HeapArray<T>::construct()
    139 {   
    140     m_pointer = new T[m_length];
    141     
    142     return m_pointer != NULL;
    143 }
    144 
    145 template
    146 < typename T >
    147 HeapArray<T>* HeapArray<T>::NewInstance(int length) 
    148 {
    149     HeapArray<T>* ret = new HeapArray<T>(length);
    150     
    151     if( !(ret && ret->construct()) ) 
    152     {
    153         delete ret;
    154         ret = 0;
    155     }
    156         
    157     return ret;
    158 }
    159 
    160 template
    161 < typename T >
    162 int HeapArray<T>::length() const
    163 {
    164     return m_length;
    165 }
    166 
    167 template
    168 < typename T >
    169 bool HeapArray<T>::get(int index, T& value)
    170 {
    171     bool ret = (0 <= index) && (index < length());
    172     
    173     if( ret )
    174     {
    175         value = m_pointer[index];
    176     }
    177     
    178     return ret;
    179 }
    180 
    181 template
    182 < typename T >
    183 bool HeapArray<T>::set(int index, T value)
    184 {
    185     bool ret = (0 <= index) && (index < length());
    186     
    187     if( ret )
    188     {
    189         m_pointer[index] = value;
    190     }
    191     
    192     return ret;
    193 }
    194 
    195 template
    196 < typename T >
    197 T& HeapArray<T>::operator [] (int index)
    198 {
    199     if( (0 <= index) && (index < length()) )
    200     {
    201         return m_pointer[index];  // 优化这里,越界抛异常;
    202     }
    203     else
    204     {
    205         throw out_of_range("T& HeapArray<T>::operator [] (int index)");
    206     }
    207 }
    208 
    209 template
    210 < typename T >
    211 T HeapArray<T>::operator [] (int index) const
    212 {
    213     if( (0 <= index) && (index < length()) )
    214     {
    215         return m_pointer[index];  // 优化这里,越界抛异常;
    216     }
    217     else
    218     {
    219         throw out_of_range("T HeapArray<T>::operator [] (int index) const");
    220     }
    221 }
    222 
    223 template
    224 < typename T >
    225 HeapArray<T>& HeapArray<T>::self()
    226 {
    227     return *this;
    228 }
    229 
    230 template
    231 < typename T >
    232 const HeapArray<T>& HeapArray<T>::self() const
    233 {
    234     return *this;
    235 }
    236 
    237 template
    238 < typename T >
    239 HeapArray<T>::~HeapArray()
    240 {
    241     delete[]m_pointer;
    242 }
    243 
    244 #endif
    245 
    246 //------------------------------------------------------------------
    247 
    248 // 测试文件 main.cpp 
    249 
    250 #include <iostream>
    251 #include <string>
    252 #include <memory>  //for auto_ptr
    253 #include "Array.hpp"
    254 #include "HeapArray.hpp"
    255 
    256 using namespace std;
    257 
    258 void TestArray()
    259 {
    260     Array<int, 5> a;
    261     
    262     for(int i=0; i<a.length(); i++)
    263     {
    264         a[i] = i;   
    265     }
    266     
    267     for (int i=0; i<a.length(); i++)
    268     {
    269         cout << a[i] << ", ";
    270     }
    271     cout << endl;
    272 }
    273 
    274 void TestHeapArray()
    275 {
    276     //使用智能指针,目的是自动释放堆空间
    277     auto_ptr< HeapArray<double> > pa(HeapArray<double>::NewInstance(5));
    278     
    279     if(pa.get() != NULL)
    280     {
    281         HeapArray<double>& array = pa->self();
    282         
    283         for(int i=0; i<array.length(); i++)
    284         {
    285             array[i] = i;
    286         }
    287         
    288         for (int i=0; i<array.length(); i++)
    289         {
    290              cout << array[i] << ", ";
    291         }
    292         cout << endl;         
    293     }         
    294 }
    295 
    296 int main(int argc, char *argv[])
    297 {
    298     try
    299     {
    300         TestArray();
    301 
    302         cout << endl;     
    303 
    304         TestHeapArray();
    305     }
    306     catch(const out_of_range& e)
    307     {
    308         cout << "exception: " << e.what() << endl;
    309     }
    310     catch(...)
    311     {
    312         cout << "other exception ... " << endl;
    313     }   
    314 
    315     return 0;
    316 }
    317 
    318 /**
    319  * 运新结果:
    320  * 0, 1, 2, 3, 4, 
    321  * ---------------------
    322  * 0, 1, 2, 3, 4, 
    323  * Run End...
    324  */
    325 
    326 /**
    327  * 栈空间数组下标越界异常运行结果:
    328  * exception: T& Array<T, N>::operator[] (int index)
    329  * Run End...
    330  */
    331 
    332 /**
    333  * 堆空间数组下标越界异常运行结果:
    334  * 0, 1, 2, 3, 4, 
    335  * ---------------------
    336  * exception: T& HeapArray<T>::operator [] (int index)
    337  * Run End...
    338  */
    标准库中数组下标越界异常案列

    5、try..catch 另类写法 和  函数异常声明/定义 throw()

       try..catch 另类写法(不建议使用)的特点:

      (1)try-catch用于分隔正常功能代码与异常处理代码

      (2)try-catch可以直接将函数实现分隔为2部分

      函数异常声明/定义的特点:

      (1)函数声明和定义时可以直接指定可能抛出的异常类型;

      (2)异常声明成为函数的一部分,可以提高代码可读性;

      (3)函数异常声明是一种与编译器之间的契约;

      (4)函数声明异常后就只能抛出声明的异常;

        A、抛出其它异常将导致程序运行终止;

        B、可以直接通过异常声明定义无异常函数;

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 // int func(int i, int j) throw()  // 无异常函数声明
     7 
     8 int func(int i, int j) throw(int, char)  //异常声明,表示该函数可能抛出int和char两种类型的异常
     9 {
    10     if ((0 < j) && (j < 10))
    11     {
    12         return (i + j);
    13     } 
    14     else
    15     {
    16         throw 'c'; // 只能抛出指定的异常类型(int、char),否则程序运行失败
    17     }
    18 }
    19 
    20 //以下的写法已经不被推荐  !!!
    21 void test(int i) try   //正常代码
    22 {
    23     cout << "func(i, i) = " << func(i, i) << endl;
    24 }
    25 catch (int j)   //异常代码
    26 {
    27     cout << "Exception: " << j << endl;
    28 }
    29 catch (char j)   //异常代码
    30 {
    31     cout << "Exception: " << j << endl;
    32 }
    33 
    34 int main(int argc, char *argv[])
    35 {
    36     test(5); //正常
    37 
    38     test(10); //抛异常
    39 
    40     return 0;
    41 }
    42 /**
    43  * 运行结果:
    44  * func(i, i) = 10
    45  * Exception: c
    46  */
    try..catch的另类写法 + throw 声明异常类型

      

  • 相关阅读:
    CPU 被客户机操作系统禁用. 重启或关闭虚拟机电源
    android怎么修改源码
    Git Gui for Windows的建库、克隆(clone)、上传(push)、下载(pull)、合并
    煮茶叶蛋口味鲜美的秘技
    屏蔽非法路由,好好上网!
    寂寞·韶华
    uml 的学习文章
    忧患人生的卓越指南——《周易》与人生哲理
    vblog 的 前景展望
    在数据库开发过程中,数据库、表、字段、视图、存储过程等的命名规则
  • 原文地址:https://www.cnblogs.com/nbk-zyc/p/12449331.html
Copyright © 2011-2022 走看看