异常处理初探
所有的代码都有可能不按照预定义的方式运行
这一组函数单纯的实现了四则运算
但是除法函数却总是会有意外
很明显,当除数为0的时候,Div函数的调用将产生无法预见的错误
C方式的解决方案
缺陷:
1)每次调用Div函数后都必须判断结果是否合法
2)参数valid如果没有指向合法的内存空间会产生错误
//解决问题后引入新问题
#include <cstdlib>
#include <iostream>
using namespace std;
double Div(double a, double b, bool* valid)
{
*valid = true;//没有判断指针是否为空就开始写入操作了
if( (-0.000000001 < b) && (b < 0.000000001) )
{
*valid = false;
return 0;
}
return a / b;
}
double Add(double a, double b)
{
return a + b;
}
double Minus(double a, double b)
{
return a - b;
}
double Multi(double a, double b)
{
return a * b;
}
int main(int argc, char *argv[])
{
bool v = false;
cout<<Div(Add(3, 3), Minus(2, 2), &v)<<endl;
cout<<v<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
0
0
Press the enter key to continue ...
程序中会出现大量的处理异常的代码
C方式的典型调用代码
缺陷:
正常逻辑代码和异常处理代码混合在一起,导致代码迅速膨胀,难以维护
//冗余枯燥!!!让代码越来越冗余
#include <cstdlib>
#include <iostream>
using namespace std;
int MemSet(void* dest, unsigned int length, unsigned char v)
{
if( dest == NULL )
{
return -1;
}
if( length < 4 )
{
return -2;
}
if( (v < 0) || (v > 9) )
{
return -3;
}
unsigned char* p = (unsigned char*)dest;
for(int i=0; i<length; i++)
{
p[i] = v;
}
return 0;
}
int main(int argc, char *argv[])
{
int ai[5];
double ad[4];
char ac[3];
int ret;
ret = MemSet(ai, sizeof(ai), 0);
if( ret == 0 )
{
}
else if( ret == -1 )
{
}
else if( ret == -2 )
{
}
else if( ret == -3 )
{
}
ret = MemSet(ad, sizeof(ad), 1);
if( ret == 0 )
{
}
else if( ret == -1 )
{
}
else if( ret == -2 )
{
}
else if( ret == -3 )
{
}
ret = MemSet(ac, sizeof(ac), 2);
if( ret == 0 )
{
}
else if( ret == -1 )
{
}
else if( ret == -2 )
{
}
else if( ret == -3 )
{
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
其它C方式的异常处理方案
goto语句,通过标签的方式处理异常代码
setjmp()和longjmp(),通过标签的方式处理异常代码
-
在C语言中可以使用上面两种解决方案将异常处理代码放到统一的地方,与正常逻辑代码分开
-
但在C++中,这两种方法可能导致对象的构造函数或者析构函数得不到调用而引发错误
int main(int argc, char *argv[])
{
int ai[5];
double ad[4];
char ac[3];
int ret = 0;
ret = MemSet(ai, sizeof(ai), 0);
if( ret != 0 )
{
goto ERROR;//少用goto
}
ERROR:
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
C++中的异常处理
C++中提供了try和catch语句块对可能产生异常的代码进行分开处理
-
try语句块处理正常逻辑
-
catch语句块处理异常
C++语言中通过throw语句引发一个异常
throw语句用于将异常“对象”抛出
-
throw语句将异常抛出,如果在当前函数中没有try…catch语句(必须由他们两个抓住)能够处理该异常,则当前函数将立即返回
-
异常被传递到上层调用函数,仍然需要try…catch语句进行处理,如果上层函数也没有能力处理该异常,则异常继续向更上层函数的函数传递。
-
-
如果在函数调用栈中的所有函数都无法处理抛出的异常,则程序异常中止
使用try…catch
#include <cstdlib>
#include <iostream>
using namespace std;
void MemSet(void* dest, unsigned int length, unsigned char v)
{ //MemSet函数可能会产生-1 -2 -3这三种异常
if( dest == NULL )
{
throw -1;
}
if( length < 4 )
{
throw -2;
}
if( (v < 0) || (v > 9) )
{
throw -3;
}
unsigned char* p = (unsigned char*)dest;
for(int i=0; i<length; i++)
{
p[i] = v;
}
}
int main(int argc, char *argv[])
{
int ai[5];
double ad[4];
char ac[3];
try
{
MemSet(ai, sizeof(ai), 0);
MemSet(ad, sizeof(ad), 1);
MemSet(ac, sizeof(ac), 2);
}
catch(int e)
{
cout<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
-2
Press the enter key to continue ...
同一个try语句块可以跟上多个catch语句块
-
同一个try语句块可以抛出多种不同类型的异常
-
不同类型的异常由不同的catch语句块负责处理(怎么处理?如何分工?)
异常被抛出后会自上而下逐一匹配catch语句块
-
异常匹配时,不会进行默认类型转换
try…catch匹配示例
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( i == 1 )
{
throw -1;
}
if( i == 2 )
{
throw "ERROR";
}
if( i == 3 )
{
throw 0.5;
}
if( i == 4 )
{
throw 'd';
}
return i;
}
int main(int argc, char *argv[])
{
for(int i=0; i<5; i++)
{
try
{
cout<<test(i)<<endl;//抛出一个异常
}
catch(int e)//看抛出的异常是不是int类型,是就处理,不是就往下看
{
cout<<"Int: "<<e<<endl;
}
catch(const char* e)//看抛出的异常是不是const char*.....
{
cout<<"const char*: "<<e<<endl;
}
catch(double e)
{
cout<<"double: "<<e<<endl;
}
//如果所有的catch没有能力去处理抛出的异常,程序就异常终止
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
0
Int: -1
const char*: ERROR
double: 0.5
terminate called after throwing an instance of 'char'
[Done] exited with code=3 in 0.784 seconds