#include<iostream>
#include<cmath>
using namespace std;
#if 0
//C语言中错误的处理,通常是采用返回值的方式或是置位全局变量的方式。这就存在两个问题。如果返回值正是我们需要的数据,且返回数据同出错数据容错差不多。全局变量,在多线程中易发生竞争。而且,当错误发生的时候,上级函数要出错处理,层层上报,造成过多的出错处理代码,且传递的效率低下。为此C++提供了异常
//C++引入异常的意义
//1.C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数之间,这样底层的函数可以看重解决具体的问题,而不必过多的考虑异常的处理。上层调用者可以再恰当的位置设计对不同类型异常的处理。
//2.异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结束的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试
double triangle(double x, double y, double z)
{
double area;
double s = (x+y+z)/2;
if(x+y>z&&x+z>y&&y+z>x)
{
area = sqrt(s*(s-x)*(s-y)*(s-z));
}
else
{
//抛出异常,他就不会走return这条路。
throw -1;
}
return area;
}
int main()
{
int a, b,c;
cin>>a>>b>>c;
//这里就有一个问题,你三角形都不存在,你还调用这个函数干嘛,为什么还可以去求面积。你可以在while里面放条件。但是太low了,现在我们用异常,我们在有可能发生异常时候加个try,后面加个catch,表示我们捕获异常,所以你这里‘输入1 2 3,就会抛异常,输出catch a exception -1; 你抛异常后面的那个值,要跟你catch里面的值要匹配。
//分析
//1.把可能发生异常的语句放在try语句当中,try原有语句的执行流程不便。
//2.若未发生异常,catch子语句并不起作用。程序会流转到catch字句的后面执行。
//3.若try块中发生异常,则通过throw抛出异常。throw抛出异常后,程序立即离开本函数,转到上一级函数。所以triangle函数中的return语句不执行
//4.throw抛出数据,类型不限。即可以是基本数据类型,也可以是构造数据类型
//4.程序流转到main函数以后,try语句块中抛出进行匹配。匹配成功,指向catch语句,catch语句执行完毕后,继续执行后面的语句。如无匹配,系统调用terminate终止程序。
//语法格式
//try
//{
// 被检查的语句
//}
//catch(异常信息类型 [变量名])
//{
// 进行异常处理的语句
//}
//语法分析:
//1.被检语句必须放在try块中,否则不起作用
//2.try catch中花括号不可省
//3.一个try-catch结构中,只能有一个try块,catch块却可以有多个。以便与不同的类型信息进行匹配
//try{}
//catch(double ){}
//catch(int){}
//catch(char){}
//catch(float){}
//4.throw抛出的类型,既可以是系统预定义的标准类型也可以是自定义类型。从抛出到catch是一个复制拷贝的过程。如果有自定义类型,要考虑自定义类型的拷贝问题。
//5.如果catch语句没有匹配信息,就可以用(...)表示可以捕获到任何异常类型的信息
try{
while(a>0&&b>0&&c>0)
{
cout<<triangle(a,b,c)<<endl;
cin>>a>>b>>c;
}
}catch(int e)
{
cout<<"catch a exception"<<e<<endl;
}
return 0;
}
#endif
#if 0
异常的处理流程,先看下面这段程序,进入main函数,他会认为f里面可能发生异常,这个异常的类型有可能是double,所以在main函数下面有个
try{
f();
}catch......,然后f调用了h,他认为h可能抛出一个char的异常,然后h调用了g,g有可能抛出int的异常,然后g立马就抛出了double a这个异常。它在当前环境下抓到了这个异常,你以前在C里面,在g里面出了错会return到h,h出了错会return到f,然后一直return出去。你这里的输出会是
catch g()
end g();
end h();
end f();
end main
但是假设它在那个地方抓不到怎么办,假设你把g改成
void g()
{
double a;
try
{
throw a;
}catch(char)
{
cout<<"cath g()"<<endl;
}
cout<<"end g()"<<endl;
return
}
那它在main的时候抓到,它就会直接输出catch main,end main。它在g里面抛出来发现没有人接,所以就不执行了,然后到上一层,然后一直到main。
如果你把a改成float类型,就会报错。它层层往上抛,抛到最后main函数还是没人接,
程序就会崩溃。
如果你中间函数没有做处理的话,默认向上抛,比如你h没做处理,就接着往下抛。
抛出类型声明
你在main函数那里,你怎么知道你要不补抓double呢,就是因为f往往会给我们一些提示,比如说我们的g,我们就用throw来表示它可能抛出什么类型。写这个的目的就是给调用者看的,你调用者调用这个函数的时候,就可以知道它catch,catch哪些东西。
下面的例子更详细
void func(int a) throw(int,float,double)
{
if(a>1)
throw (int)a;
if(a=1)
throw (float)a;
if(a>1)
throw (double)a;
}
#endif
#if 0
void g() throw(double,int,float)
{
double a;
try
{
throw a;
}catch(double)
{
cout<<"catch g()"<<endl; //throw
}
cout<<"end g()"<<endl;
return ;
}
void h()
{
try{
g();
}catch(int)
{
cout<<"catch h()"<<endl;
}
cout<<"end h()"<<endl;
}
void f()
{
try{
h();
}catch(char)
{
cout<<"catch f()"<<endl;
}
cout<<"end f()"<<endl;
}
int main()
{
try{
f();
}catch(double)
{
cout<<"catch main"<<endl;
}
cout<<"end main"<<endl;
return 0;
}
#endif
class A
{
public:
A()
{
cout<<"A constructor"<<endl;
}
~A()
{
cout<<"~A destructor"<<endl;
}
};
int divide(int x, int y)
{
A a;
if(y == 0)
throw('a');
return x/y;
}
int myDivide(int x, int y)
{
A b;
divide(x,y) ;
}
int main()
{
//divide是除法的意思,你不能除数是0,输出0就没法算了
//但是你看你这里myDivide里面创建了对象,然后再调用的divide,然后你divide又创建了一个对象,你看输出,你可以看到它构造了两次,析构了两次,你throw了之后函数就结束了,虽然不是以return做结束,你在divide的时候throw之后他就会把栈给销毁掉,这就叫栈自璇。但是你如果那边改成new之后,他就内存泄漏了。这就是它问题的地方
//譬如
// int divide(int x,int y)
// {
// A *pa=new A;
// .......
// }
//
//
//
try{
myDivide(4,0);
}catch(int x){
cout<<"x"<<endl;
cout<<x<<endl;
}catch(double y){
cout<<"y"<<endl;
cout<<y<<endl;
}catch(...){
cout<<"no x, no y"<<endl;
}
return 0;
}
C++抛出异常
最新推荐文章于 2024-08-18 17:08:08 发布