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 + "\t >> 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 声明异常类型