第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;
}
输出结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/9d06b5698735a986e7d2b680ce8447cb.png)
小总结:
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;
}
输出结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/d6e47f42fe02b014d9b89ff3b19057e3.png)
二.异常与对象
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