第26课 - 异常处理 - 下
一.深入异常处理
问题:有时在工程中只关心是否产生了异常,而不关心具体异常的类型,C++语言可以做到吗?
1.1 C++中的catch语句可以使用...捕获所有的异常
...作参数表示可变参数函数,可以接受任意参数
Source Example 1.1: #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; int test(int i) { if(i == 1) { throw -1; } if (i == 2) { throw "Error"; } if (i == 3) { throw 0.5; } return i; } int main(int argc, char** argv) { for (int i = 0; i < 5; i++) { try { cout<<test(i)<<endl; } /* 如果还要处理其他异常,可变参数的处理需要放到最后,否则编译不过! */ catch (...) { cout<<"Error Occur:"<<endl; } } return 0; }输出结果如下:
小总结:
a.catch(...)可以捕获到所有异常但是无法得到异常信息
b.catch(...)一般作为最后一个异常处理块出现
看见代码中的catch就要意识到这里实在处理异常的情况,而异常是在try中产生的。
1.2 catch语句块中仍然可以抛出异常
#include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; int test(int i) { if((i >= 6) && (i <= 9)) { throw i; } return i; } int main(int argc, char** argv) { try { for (int i = 0; i < 10; i++) { try { cout<<test(i)<<endl; } catch (int e) { cout<<"Error No:"<<e<<endl; throw e; } } } catch(int e) { /* 执行完该异常处理后向后执行,退出程序,并不会继续循环 */ cout<<"Exception No:"<<e<<endl; } return 0; }
输出结果如下:
1.3 在catch(...)语句块中,可以使用不带参数的throw语句抛出捕获的异常
Source Example 1.3: #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; int test(int i) { if((i >= 6) && (i <= 9)) { throw i; } return i; } int main(int argc, char** argv) { try { for (int i = 0; i < 10; i++) { try { cout<<test(i)<<endl; } catch (...) { cout<<"Error"<<endl; throw ; } } } catch(int e) { /* 执行完该异常处理后向后执行,退出程序,并不会继续循环 */ cout<<"Exception No:"<<e<<endl; } return 0; }输出结果如下:
二.异常与对象
2.1 不要在构造函数中抛出异常
2.1.1 在构造函数中可能申请系统资源,而在构造函数中出现异常会导致对象构造不完全
2.1.2 不完全对象的析构是不会被调用的,因此会造成资源泄露
Source Example 2.1: #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; class Test { protected: int *p; public: Test() { cout <<"Test()"<<endl; p = new int[5]; throw 1; } /* 在构造函数中抛出了异常,不会调用析构函数,内存泄露 */ ~Test() { cout<<"~Test()"<<endl; delete[] p; } }; int main(int argc, char** argv) { try { Test t; } catch(int error) { cout<<"Error No:"<<error<<endl; } return 0; }
输出结果如下:
三.工程中的异常应用
3.1 在工程中会定义一系列异常类
3.2 通过继承,可以得到一个异常家族
3.3 每个类代表工程中可能出现的一种异常类型
3.4 由于对象构造和拷贝的开销,在定义catch语句块时用引用作为参数
3.5 在工程中可以使用标准库中的异常类
3.6 可将标准库中的异常类作为基类派生新的异常类
3.7 标准库中的异常都是exception类派生的
3.8 exception类主要有两个分支
3.8.1 logic_error用于描述程序中出现的逻辑错误
如:传递无效参数
3.8.2 runtime_error用于描述无法预料的事件造成的错误
如:内存耗尽,硬件错误
3.8.3 logic_error和runtime_error都提供了一个参数为字符串的构造函数,这样就可以保持错误信息
3.8.4 通过what()成员函数就可以得到异常信息
Source Example 3.8: #include <iostream> #include <stdexcept> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; double Div(double a, double b) { if ((-0.0000001 < b)&&(b < 0.0000001)) { throw invalid_argument("Div by zero error..."); } } int main(int argc, char** argv) { try { cout<<Div(1,0)<<endl; } /* 引用不需要拷贝构造 */ catch(logic_error& e) { cout<<e.what()<<endl; } return 0; } 输出: Div by zero error...
Source Example: #include <iostream> #include <stdexcept> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; /* 自己定义异常类 */ class DivZero : public logic_error{ public: DivZero(const char *s) : logic_error(s) { } }; double Div(double a, double b) { if ((-0.0000001 < b)&&(b < 0.0000001)) { throw DivZero("Div by zero error..."); } } int main(int argc, char** argv) { try { cout<<Div(1,0)<<endl; } /* 赋值兼容性原则 */ catch(logic_error& e) { cout<<e.what()<<endl; } return 0; } 输出: Div by zero error...
四.函数级try用法
4.1 可以将函数体作为一个完整的try语句块
int func(int i) int func(int i)try { { try return i; { 等价于 } return i; ------------> catch(...) } ------------> { catch(...) return -1; { } return -1; } }
Source Example 4.1: #include <iostream> #include <stdexcept> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; int func(int i) try { if (i > 0) { return i; } else { throw "error"; } } catch(...) { return -1; } int main(int argc, char** argv) { for (int i = 0; i < 5; i++) { cout<<func(i)<<endl; } return 0; } 输出: -1 1 2 3 4