首页 > 代码库 > C++中的异常(整理自其他博文)

C++中的异常(整理自其他博文)

  本文整理自博文“C++的try_catch异常”。

   1. 一个简单例子及catch(...)的作用

 1 #include <iostream> 2 #include <stdlib.h> 3  4 using namespace std; 5  6 double func(double x, double y) 7 { 8     if (y == 0) 9     {10         throw y;        // 抛出异常11     }12     return x / y;13 }14 15 int main()16 {17     double res;18     try19     {20         res = func(2, 3);21         cout << "The result of x/y is : " << res << endl;22         res = func(4, 0);23     }24     catch (double)        // 捕获异常25     {26         cerr << "error of dividing zero.\n" << endl;;27         exit(1);28     }29     catch (...)            // 类似于switch case语句中会用到的的default语句30     {31         cerr << "exception occurs" << endl;32     }33 34     return 0;35 }
A simple example

  catch(…)能够捕获多种数据类型的异常对象,所以它提供给程序员一种对异常对象更好的控制手段,使开发的软件系统有很好的可靠性。因此一个比较有经验的程序员通常会这样组织编写它的代码模块,如下:

 1 void Func() 2 { 3     try 4     { 5         // 这里的程序代码完成真正复杂的计算工作,这些代码在执行过程中 6         // 有可能抛出DataType1、DataType2和DataType3类型的异常对象。 7     } 8     catch (DataType1& d1) 9     {10     }11     catch (DataType2& d2)12     {13     }14     catch (DataType3& d3)15     {16     }17     // 注意上面try block中可能抛出的DataType1、DataType2和DataType3三18     // 种类型的异常对象在前面都已经有对应的catch block来处理。但为什么19     // 还要在最后再定义一个catch(…) block呢?这就是为了有更好的安全性和20     // 可靠性,避免上面的try block抛出了其它未考虑到的异常对象时导致的程21     // 序出现意外崩溃的严重后果,而且这在用VC开发的系统上更特别有效,因22     // 为catch(…)能捕获系统出现的异常,而系统异常往往令程序员头痛了,现23     // 在系统一般都比较复杂,而且由很多人共同开发,一不小心就会导致一个24     // 指针变量指向了其它非法区域,结果意外灾难不幸发生了。catch(…)为这种25     // 潜在的隐患提供了一种有效的补救措施。26     catch (…)27     {28     }29 }

   2. 异常中采用面向对象的处理

  先看个例子

 1 #include <iostream> 2 #include <exception> 3  4 using namespace std; 5  6 class ExceptionClass 7 { 8 public: 9 10     ExceptionClass(char * name = "Exception Default Class")11     {12         cout << "Exception Class : Construct String" << endl;13     }14 15     virtual ~ExceptionClass()16     {17         cout << "Exception Class : Destruct String" << endl;18     }19 20     void ReportError()21     {22         cout << "Exception Class : Report Error Message" << endl;23     }24 25 };26 27 class TestedClass28 {29 public:30 31     TestedClass(char * name = "dufault name")32     {33         cout << "Construct String::" << name << endl;34         this->name = name;35     }36 37     virtual ~TestedClass()38     {39         cout << "Destruct String" << endl;40     }41 42     void mythrow()43     {44         throw ExceptionClass("my throw");45     }46 47 private:48     char * name;49 50 };51 52 int main()53 {54     TestedClass e("Test");55     try56     {57         e.mythrow();58     }59     catch (ExceptionClass eTestedClass)60     {61         eTestedClass.ReportError();62     }63     catch (...)64     {65         cout << "*******************" << endl;66     }67 68     return 0;69 }
View code

  在该例子中专门设计了一个异常类来处理异常。

  在博文“C++的try_catch异常”中,作者还提供了另一个很值得参考的例子。

 1 void OpenFile(string f) 2 { 3     try 4     { 5         // 打开文件的操作,可能抛出FileOpenException 6     } 7     catch (FileOpenException& fe) 8     { 9         // 处理这个异常,如果这个异常可以很好的得以恢复,那么处理完毕后函数10         // 正常返回;否则必须重新抛出这个异常,以供上层的调用函数来能再次处11         // 理这个异常对象12         int result = ReOpenFile(f);13         if (result == false) throw;14     }15 }16 17 void ReadFile(File f)18 {19     try20     {21         // 从文件中读数据,可能抛出FileReadException22     }23     catch (FileReadException& fe)24     {25         // 处理这个异常,如果这个异常可以很好的得以恢复,那么处理完毕后函数26         // 正常返回;否则必须重新抛出这个异常,以供上层的调用函数来能再次处27         // 理这个异常对象28         int result = ReReadFile(f);29         if (result == false) throw;30     }31 }32 33 void WriteFile(File f)34 {35     try36     {37         // 往文件中写数据,可能抛出FileWriteException38     }39     catch (FileWriteException& fe)40     {41         // 处理这个异常,如果这个异常可以很好的得以恢复,那么处理完毕后函数42         // 正常返回;否则必须重新抛出这个异常,以供上层的调用函数来能再次处理这个异常对象43         int result = ReWriteFile(f);44         if (result == false) throw;45     }46 }47 48 void Func()49 {50     try51     {52         // 对文件进行操作,可能出现FileWriteException、FileWriteException53         // 和FileWriteException异常54         OpenFile(…);55         ReadFile(…);56         WriteFile(…);57     }58     // 注意:FileException是FileOpenException、FileReadException和FileWriteException59     // 的基类,因此这里定义的catch(FileException& fe)能捕获所有与文件操作失败的异60     // 常。61     catch (FileException& fe)62     {63         ExceptionInfo* ef = fe.GetExceptionInfo();64         cout << “操作文件时出现了不可恢复的错误,原因是:” << fe << endl;65     }66 }
View Code

   3. 构造和析构函数中的异常抛出

  先看个例子:

 1 #include <iostream> 2 #include <stdlib.h> 3  4 using namespace std; 5  6 class ExceptionClass 7 { 8 public: 9 10     ExceptionClass()11     {12         cout << "Construct." << endl;13         s = new char[4];14         cout << "Throw a exception." << endl;15         throw 18;16     }17     ~ExceptionClass()18     {19         cout << "Destruct." << endl;20         delete[] s;21     }22 23 private:24 25     char* s;26 27 };28 29 void main()30 {31     try32     {33         ExceptionClass e;34     }35     catch (...)36     {37     }38 }
View Code

  程序运行结果为:

  Construct.

  Throw a exception.

  在这两句输出之间,我们已经给 s 分配了内存,但内存没有被释放(因为它是在析构函数中释放的)。应该说这符合实际现象,因为对象没有完整构造。

  为了避免这种情况,我想你也许会说:应避免对象通过本身的构造函数涉及到异常抛出。即:既不在构造函数中出现异常抛出,也不应在构造函数调用的一切东西中出现异常抛出。但是在C++中可以在构造函数中抛出异常,经典的解决方案是使用STL的标准类auto_ptr

  那么,在析构函数中的情况呢?我们已经知道,异常抛出之后,就要调用本身的析构函数,如果这析构函数中还有异常抛出的话,则已存在的异常尚未被捕获,会导致异常捕捉不到。

    4. 标准C++异常类

  标准异常都派生自一个公共的基类exception。基类包含必要的多态性函数提供异常描述,可以被重载。下面是exception类的原型:

1 class exception 2 {3 public:4     exception() throw();5     exception(const exception&) throw();6     exception& operator= (const exception&) throw();7     virtual ~exception() throw();8     virtual const char* what() const throw();9 };

  其他派生自基类exception的标准异常类为:

 1 namespace std 2 { 3     //exception派生 4     class logic_error;         //逻辑错误,在程序运行前可以检测出来 5  6     //logic_error派生 7     class domain_error;        //违反了前置条件 8     class invalid_argument;   //指出函数的一个无效参数 9     class length_error;        //指出有一个超过类型size_t的最大可表现值长度的对象的企图10     class out_of_range;        //参数越界11     class bad_cast;            //在运行时类型识别中有一个无效的dynamic_cast表达式12     class bad_typeid;         //报告在表达试typeid(*p)中有一个空指针p13 14     //exception派生15     class runtime_error;      //运行时错误,仅在程序运行中检测到16 17     //runtime_error派生18     class range_error;         //违反后置条件19     class overflow_error;      //报告一个算术溢出20     class bad_alloc;           //存储分配错误21 22 }
  一个利用标准异常类的例子如下:
 1 #include <iostream> 2 #include <exception> 3  4 using namespace std; 5  6 class TestedClass 7 { 8 public: 9 10     TestedClass(char * name = "dufault name")11     {12         cout << "Construct String::" << name << endl;13         this->name = name;14     }15 16     virtual ~TestedClass()17     {18         cout << "Destruct String" << endl;19     }20 21     void mythrow()22     {23         throw logic_error("my throw");24     }25 26 private:27     char * name;28 29 };30 31 int main()32 {33     TestedClass e("Test");34     try35     {36         e.mythrow();37     }38     catch (logic_error& e)39     {40         cerr << "logic error exception caught: " << e.what() << endl;41     }42     catch (exception& e)43     {44         cerr << "exception caught!" << e.what() << endl;45     }46     catch (...)47     {48         cout << "*******************" << endl;49     }50 51     return 0;52 }
View Code
 

C++中的异常(整理自其他博文)