C++异常处理的实现

1异常基本语法
在这里插入图片描述
1) 若有异常则通过throw操作创建一个异常对象并抛掷。
2) 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。
3) 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。
4) catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。
5) 如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。
6)处理不了的异常,可以在catch的最后一个分支,使用throw语法,向上扔。
案例1:被零整除案例
int divide(int x, int y )
{
if (y ==0)
{
throw x;
}
return x/y;
}

void main41()
{
try
{
cout << “8/2 = " << divide(8, 2) << endl;
cout << “10/0 =” << divide(10, 0) << endl;
}
catch (int e)
{
cout << “e” << " is divided by zero!” << endl;
}
catch(…)
{
cout << “未知异常” << endl;
}
cout << “ok” << endl;
system(“pause”);
return ;
}
案例2:
class A{};
void f(){
if(…) throw A;
}
void g(){
try{
f();
}catch(B){
cout<<“exception B\n”;
}
}
int main(){
g();
}
throw A将穿透函数f,g和main,抵达系统的最后一道防线——激发terminate函数.
该函数调用引起运行终止的abort函数.
最后一道防线的函数可以由程序员设置.从而规定其终止前的行为.
修改系统默认行为:
可以通过set_terminate函数修改捕捉不住异常的默认处理器,从而使得发生捉不住异常时,被自定义函数处理:
 void myTerminate(){cout<<“HereIsMyTerminate\n”;}
 set_terminate(myTerminate);
 set_terminate函数在头文件exception中声明,参数为函数指针void(*)().
案例3:
构造函数没有返回类型,无法通过返回值来报告运行状态,所以只通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。

7)异常机制与函数机制互不干涉,但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题
比如:
class A{};
class B{};

int main()
{
try
{
int j = 0;
double d = 2.3;
char str[20] = “Hello”;
cout<<"Please input a exception number: ";
int a;
cin>>a;
switch(a)
{
case 1:
throw d;
case 2:
throw j;
case 3:
throw str;
case 4:
throw A();
case 5:
throw B();
default:
cout<<“No throws here.\n”;
}
}
catch(int)
{
cout<<“int exception.\n”;
}
catch(double)
{
cout<<“double exception.\n”;
}
catch(char*)
{
cout<<“char* exception.\n”;
}
catch(A)
{
cout<<“class A exception.\n”;
}
catch(B)
{
cout<<“class B exception.\n”;
}
cout<<“That’s ok.\n”;
system(“pause”);
}//====================================
catch代码块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。
异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据。
8)异常捕捉严格按照类型匹配
异常捕捉的类型匹配之苛刻程度可以和模板的类型匹配媲美,它不允许相容类型的隐式转换,比如,抛掷char类型用int型就捕捉不到.例如下列代码不会输出“int exception.”,从而也不会输出“That’s ok.” 因为出现异常后提示退出
int main(){
try{
throw ‘H’;
}catch(int){
cout<<“int exception.\n”;
}
cout<<“That’s ok.\n”;
}

2栈解旋(unwinding)
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。
class MyException {};

class Test
{
public:
Test(int a=0, int b=0)
{
this->a = a;
this->b = b;
cout << “Test 构造函数执行” << “a:” << a << " b: " << b << endl;
}
void printT()
{
cout << “a:” << a << " b: " << b << endl;
}
~Test()
{
cout << “Test 析构函数执行” << “a:” << a << " b: " << b << endl;
}
private:
int a;
int b;
};

void myFunc() throw (MyException)
{
Test t1;
Test t2;
cout << “定义了两个栈变量,异常抛出后测试栈变量的如何被析构” << endl;
throw MyException();
}
void main()
{
//异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,
//都会被自动析构。析构的顺序与构造的顺序相反。
//这一过程称为栈的解旋(unwinding)
try
{
myFunc();
}
//catch(MyException &e) //这里不能访问异常对象
catch(MyException ) //这里不能访问异常对象
{
cout << “接收到MyException类型异常” << endl;
}
catch(…)
{
cout << “未知类型异常” << endl;
}
system(“pause”);
return ;
}
3异常接口声明
1)为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型,例如:
void func() throw (A, B, C , D); //这个函数func()能够且只能抛出类型A B C D及其子类型的异常。
2)如果在函数声明中没有包含异常接口声明,则次函数可以抛掷任何类型的异常,例如:
void func();
3)一个不抛掷任何类型异常的函数可以声明为:
void func() throw();
4) 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序。

4异常类型和异常变量的生命周期
1)throw的异常是有类型的,可以使,数字、字符串、类对象。
2)throw的异常是有类型的,catch严格按照类型进行匹配。
3)注意 异常对象的内存模型 。
4.1传统处理错误

//文件的二进制copy
int filecopy01(char *filename2, char *filename1 )
{
FILE *fp1= NULL, *fp2 = NULL;
fp1 = fopen(filename1, “rb”);
if (fp1 == NULL)
{
return 1;
}
fp2 = fopen(filename2, “wb”);
if (fp1 == NULL)
{
return 2;
}
char buf[256];
int readlen, writelen;
while ( (readlen = fread(buf, 1, 256, fp1)) > 0 ) //如果读到数据,则大于0
{
writelen = fwrite(buf, 1, readlen, fp2);
if (readlen != readlen)
{
return 3;
}
}
fclose(fp1);
fclose(fp2);
return 0;
}

测试程序
void main11()
{
int ret;
ret = filecopy01(“c:/1.txt”,“c:/2.txt”);
if (ret !=0 )
{
switch(ret)
{
case 1:
printf(“打开源文件时出错!\n”);
break;
case 2:
printf(“打开目标文件时出错!\n”);
break;
case 3:
printf(“拷贝文件时出错!\n”);
break;
default:
printf(“发生未知错误!\n”);
break;
}
}
}
4.2 throw int类型异常
/文件的二进制copy
void filecopy02(char *filename2, char *filename1 )
{
FILE *fp1= NULL, *fp2 = NULL;
fp1 = fopen(filename1, “rb”);
if (fp1 == NULL)
{
//return 1;
throw 1;
}
fp2 = fopen(filename2, “wb”);
if (fp1 == NULL)
{
//return 2;
throw 2;
}
char buf[256];
int readlen, writelen;
while ( (readlen = fread(buf, 1, 256, fp1)) > 0 ) //如果读到数据,则大于0
{
writelen = fwrite(buf, 1, readlen, fp2);
if (readlen != readlen)
{
//return 3;
throw 3;
}
}
fclose(fp1);
fclose(fp2);
return ;
}

4.3 throw字符类型异常
//文件的二进制copy
void filecopy03(char *filename2, char *filename1 )
{
FILE *fp1= NULL, *fp2 = NULL;
fp1 = fopen(filename1, “rb”);
if (fp1 == NULL)
{
throw “打开源文件时出错”;
}
fp2 = fopen(filename2, “wb”);
if (fp1 == NULL)
{
throw “打开目标文件时出错”;
}
char buf[256];
int readlen, writelen;
while ( (readlen = fread(buf, 1, 256, fp1)) > 0 ) //如果读到数据,则大于0
{
writelen = fwrite(buf, 1, readlen, fp2);
if (readlen != readlen)
{
throw “拷贝文件过程中失败”;
}
}
fclose(fp1);
fclose(fp2);
return ;
}

4.4 throw类对象类型异常
//throw int类型变量
//throw 字符串类型
//throw 类类型
class BadSrcFile
{
public:
BadSrcFile()
{
cout << "BadSrcFile 构造 do "<<endl;
}
~BadSrcFile()
{
cout << "BadSrcFile 析构 do "<<endl;
}
BadSrcFile(BadSrcFile & obj)
{
cout << "拷贝构造 do "<<endl;
}
void toString()
{
cout << “aaaa” << endl;
}

};
class BadDestFile {};
class BadCpyFile {};;

void filecopy04(char *filename2, char *filename1 )
{
FILE *fp1= NULL, *fp2 = NULL;
fp1 = fopen(filename1, “rb”);
if (fp1 == NULL)
{
//throw new BadSrcFile();
throw BadSrcFile();
}

fp2 = fopen(filename2, “wb”);
if (fp1 == NULL)
{
throw BadDestFile();
}
char buf[256];
int readlen, writelen;
while ( (readlen = fread(buf, 1, 256, fp1)) > 0 ) //如果读到数据,则大于0
{
writelen = fwrite(buf, 1, readlen, fp2);
if (readlen != readlen)
{
throw BadCpyFile();
}
}
fclose(fp1);
fclose(fp2);
return ;
}
main测试案例

//结论://C++编译器通过throw 来产生对象,C++编译器再执行对应的catch分支,相当于一个函数调用,把实参传递给形参。
void main11()
{
try
{
//filecopy02(“c:/1.txt”,“c:/2.txt”);
// filecopy03(“c:/1.txt”,“c:/2.txt”);
filecopy04(“c:/1.txt”,“c:/2.txt”);
}
catch (int e)
{
printf(“发生异常:%d \n”, e);
}
catch (const char * e)
{
printf(“发生异常:%s \n”, e);
}
catch ( BadSrcFile *e)
{
e->toString();
printf(“发生异常:打开源文件时出错!\n”);
}
catch ( BadSrcFile &e)
{
e.toString();
printf(“发生异常:打开源文件时出错!\n”);
}
catch ( BadDestFile e)
{
printf(“发生异常:打开目标文件时出错!\n”);
}
catch ( BadCpyFile e)
{
printf(“发生异常:copy时出错!\n”);
}
catch(…) //抓漏网之鱼
{
printf(“发生了未知异常! 抓漏网之鱼\n”);
}
//class BadSrcFile {};
//class BadDestFile {};
//class BadCpyFile {};;
}

猜你喜欢

转载自blog.csdn.net/it_xiangqiang/article/details/109214668
今日推荐