文章目录
深入异常处理
问题:有时在工程中只关心是否产生 了异常,而不关心具体的异常类型,C++语言可以做到吗?
C++中的 catch语句可以使用…捕获所有的异常
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( i == 1 )
{
throw "p";
}
if( i == 2 )
{
throw 0.5;
}
if( i == 3 )
{
throw 3;
}
if( i == 4 )
{
throw 'c';
}
return i;
}
int main(int argc, char *argv[])
{
for(int i=0; i<10; i++)
{
try
{
cout<<test(i)<<endl;
}
catch(char e)
{
cout<<"Exception: "<<e<<endl;
}
catch(...) //...捕获所有异常
{
cout<<"Exception Occur"<<endl;
}
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
catch(…) 可以捕获所有异常但却无法得到异常信息
catch(…) 一般作为最后一个异常处理块出现
tips:
看见代码中的catch就要意识到这里在处理异常情况,而异常是在对应的try中产生的。
在catch语句块中仍然可以抛出异常
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( (6 <= i) && (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<<"Exception: "<<e<<endl;
throw e;
}
}
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
在catch(…) 语句块中,可以通过不带参数的 throw语 句抛出捕获的异常
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( (6 <= i) && (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<<"Exception Occur"<<endl;
throw; //throw可以不带参数
}
}
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
异常与对象
不要在构造函数中抛出异常
在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全
不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏
构造函数中的异常示例
#include <cstdlib>
#include <iostream>
using namespace std;
class Test
{
int* p;
public:
Test()
{
cout<<"Test()"<<endl;
p = new int[5];
throw 10; //构造函数中抛出异常
}
~Test()
{
cout<<"~Test()"<<endl;
delete[] p;
}
};
int main(int argc, char *argv[])
{
try
{
Test t;
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
分析:析构函数没有被调用
工程中的异常应用
在工程中会定义一系列的异常类
通过继承,可以得到一个异常类族
每个类代表工程中可能出现的一种异常类型
由于对象构造与拷贝的开销,在定义catch语句块时使用引用作为参数
在工程中可以使用标准库中的异常类
可将标准库中的异常类作为基类派生新的异常类
标准库中的异常都是从exception类派生的
exception类有两个主要的分支
logic_error用于描述程序中出现的逻辑错误
如:传递无效参数
runtime_error用于描述无法预料的事件所造成的错误
如:内存耗尽 ,硬件错误
标准库中的异常
logic_error和runtime_error都提供了一个参数为字 符串的构造函数,这样就可以保持错误信息
通过what()成员函数就可以得到错误的信息
异常的工程应用初探
#include <cstdlib>
#include <iostream>
#include <stdexcept>
using namespace std;
class divide_by_zero : public logic_error{
public:
divide_by_zero(const char* s) : logic_error(s) { //调用父类构造函数
}
};
double Div(double a, double b){
if((-0.00000001 < b) && (b < 0.00000001) ){
throw divide_by_zero("Divide by zero..."); //产生一个临时对象
//此处为什么要用重新继承的 divide_by_zero类哪? 因为 logic_error不太符合
}
return a/b;
}
int main(int argc, char *argv[])
{
try
{
cout << Div(2,0)<<endl;
}
catch(exception& e) //引用:避免拷贝
//此处赋值兼容性原则
//异常类族:可以捕获所有异常
{
cout << e.what()<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
函数级try语法
可以将函数体作为一个完整的try语句块
函数级try语法可以更好将正常逻辑代码与异 常处理代码分开,提高代码的可读性与维护 性
#include <cstdlib>
#include <iostream>
#include <stdexcept>
using namespace std;
int func(int i){
try{
if(i > 0){
return i;
}
else{
throw "error";
}
}
catch(...){
return -1;
}
}
int func2(int i) try //函数级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 << func2(i) <<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
小结
catch(…) 可以捕获所有异常
catch(…) 经常作为最后一个 catch语句出现
不要在构造函数中抛出异常,这样可能造成资源泄露
工程中经常以标准库中的异常类作为项目异常的基础
函数级try语句块能够更好的提高代码的维护性