-------------------------------------资源来源于网络,仅供自学使用,如有侵权,联系我必删.
第一:
异常处理初探
所有的代码都有可能 不按照 预定义的方式运行
? 这一组函数单纯的实现了四则运算
? 但是除法函数却总是会有意外 。。。
很明显 , 当除数为0 的时候 , Div 函数的调用将产生无法预见的错误
C 方式的解决方案
#include <cstdlib>
#include <iostream>
using namespace std;
double Div(double a, double b, bool* valid)//除法
{
*valid = true;//向不知道是否为空valid指针空间里写入值
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; //除数为0
cout<<v<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
缺陷:
1) 每次调用 Div 函数后都必须判断结果是否合法
2) 参数 valid 如果没有指向合法的内存空间会产生错误
第二:
程序中会出现大量的处理异常的代码
C 方式的典型调用代码
#include <cstdlib>
#include <iostream>
using namespace std;
int MemSet(void* dest, unsigned int length, unsigned char v)
{
//指针合法性判断
if( dest == NULL )//指针不为空
{
return -1;
}
//项目需要,大小不小于4
if( length < 4 )
{
return -2;
}
//工程需求范围在0-9
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;
}
缺陷:
正常逻辑代码和异常处理代码混合在一起 , 导致代码迅速膨胀 , 难以维护 。。。
第三
其它C 方式的异常处理方案
? go to 语句
? setjmp() 和 longjmp()
#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 = 0;
ret = MemSet(ai, sizeof(ai), 0);
if( ret != 0 )
{
goto ERROR;
}
ERROR:
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
? 在C语言中可以使用上面两种解决方案将异常处理代码放到统一的地方 , 与正常逻辑代码分开
? 但在 C++ 中 , 这两种方法可能导致对象的构造函数或者析构函数得不到调用而引发错误
第四:
C++ 中的异常处理
? C++ 中提供了 try 和 catch 语句块对可能产生异常的代码进行分开处理
? try 语句块处理正常逻辑
? catch 语句块处理异常
? C++ 语言中通过 throw 语句引发一个异常
#include <cstdlib>
#include <iostream>
using namespace std;
#define DIV_ZERO_ERROR -1
double Div(double a, double b)
{
if( (-0.000000001 < b) && (b < 0.000000001) )//分母(除数)为0
{
throw DIV_ZERO_ERROR;//抛出除零异常 -1
}
return a / b;
}
int main(int argc, char *argv[])
{
try//正常逻辑代码
{
cout<<Div(3, 1.1)<<endl;
cout<<Div(1, 0)<<endl;//异常,立即返回!
cout<<Div(1, 2)<<endl;
}
catch(int error)//-1 处理除零异常
{
cout<<"Exception: "<<endl;
cout<<error<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
第五:
? 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)
{
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//正常代码:3个数组内存设置
{
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;
}
第六:
? 同一个 try 语句块可以跟上多个 catch 语句块
? 同一个 try 语句块可以抛出多种不同类型的异常
? 不同类型的异常由不同的 catch 语句块负责处理
? 异常被抛出后会自上而下逐一匹配 catch 语句块
? 异常匹配时 , 不会进行默认类型转换
try … catch 匹配示例
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( i == 1 )
{
throw -1; //int类型 异常
}
if( i == 2 )
{
throw "ERROR"; //char*字符 异常
}
if( i == 3 )
{
throw 0.5; //double类型 异常
}
if( i == 4 )
{
throw 'd'; //char字符型 异常
}
return i;
}
int main(int argc, char *argv[])
{
for(int i=0; i<5; i++)
{
//一个try 对应 多个catch函数
try
{
cout<<test(i)<<endl;
}
catch(int e) //int类型 异常
{
cout<<"Int: "<<e<<endl;
}
catch(const char* e) //char*字符 异常
{
cout<<"const char*: "<<e<<endl;
}
catch(double e) //double类型 异常
{
cout<<"double: "<<e<<endl;
}
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
小结
? 异常处理是程序中随处可见的情况
? 在工程实践中 , 大多数的代码都是用于处理异常的
? 异常处理的质量直接决定最终产品的质量
? C++ 提供了 try … catch 语句用于将正常逻辑代码与异常处理代码进行分开处理