C++异常处理的实现

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数C初学者入门训练题解CC的使用文章「初学」C++

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

🐰C++异常处理的实现

🌸try的嵌套异常处理语句

🌸异常与函数

🌸异常类


🐰C++异常处理的实现

C++处理异常的机制由三个部分组成:检查(try)、抛出(throw)和捕捉(catch)。把需要检查的语句放在try块中,throw用来当出现异常时抛出一个异常信息,而catch则用来捕捉异常信息,如果捕捉到异常信息,就处理它。try-throw-catch构成了C++异常处理的基本结构

例如:

try
{
    ...
    if(表达式1)
        throw x1;
    ...
    if(表达式2)
        throw x1;
    ...
    if(表达式n)
        throw xn;
    ...
}
catch(异常类型声明1)
{
    异常类型语句序列1
}
catch(异常类型声明2)
{
    异常类型语句序列2
}
...
catch(异常类型声明n)
{
    异常类型语句序列n
}

这里,try语句块内为需要受保护的待检测异常的语句序列,如果怀疑某段程序代码在执行时有可能发生异常,就将它放入try语句块中。当这段代码执行出现异常时,即某个if语句的表达式为真时,会用其中的throw语句来抛出这个异常

例如:

throw语句的语法格式
throw 表达式;

throw语句是在程序执行发生了异常时用来抛出这个异常的,其中表达式的值可以是int,float,字符串,类类型等,把异常抛给相应的处理者,即类型匹配的catch语句块。如果程序中多处需要抛出异常,应该用不同类型的操作数来互相区别。throw抛出的异常,通常是被catch语句捕获

catch语句块是紧跟在try语句后面的,即try块和catch块作为一个整体出现在一个try-catch结构中,可以只有try而无catch块。即在本函数中只检查异常而不处理异常,把catch块放在其他函数中。一个try-catch结构try只能有一个,但可以有多个catch块,以便于与不同类型的异常信息匹配在执行try块中的语句时如果出现异常执行了throw语句,系统会根据throw抛出的异常信息类型按catch块出现的次序,一次检查每一个catch参数表中的异常声明类型与抛出的异常信息类型是否匹配,若匹配,该catch块就会捕获这个异常,执行catch块中的异常处理语句来处理异常

在catch参数表中,一般只写异常信息的类型名,如,catch(double)

系统检查所抛出的异常信息类型是否与catch参数表中的异常声明类型相匹配,而不检查它们的值假如变量a,b,c都是int类型,即使它们的值不同,在throw语句中写throw a、throw b、throw c的作用均是相同的。因此,如果需要检测多个不同的异常信息,应当由throw抛出不同类型的异常信息。

异常信息类型可以是C++系统预定义的标准类型,也可以是自己定义的类型(如结构体或类)。如果有throw抛出的异常信息属于该类型或其子类型,则catch与throw二者匹配,catch捕获异常信息。

注意:系统在检查异常信息数据类型的匹配时,不会进行数据类型的默认转换,只有与所抛的异常信息类型精度匹配的catch块才会捕获这个异常

在catch参数表中,除了指定异常信息的类型名以外,还可以是变量名,如:catch(double d)。此时,若throw抛出的异常信息是double型变量 a,则catch在捕获异常信息a的同时,还使d获得a的值。如果希望在捕获异常信息时,还能利用throw抛出的异常信息的值,这时就需要在catch参数表中写出变量名。

例如:

catch(double d)
{
    cout<<"throw "<<d;
}
这时输出d的值(也就是a值)。当抛出的类对象时,有时希望在catch块中显示该对象中的某些值

求解一元二次方程ax^2+bx+c=0。其一般解为x1=(-b+(b^2-4ac))/2a,x2=x1=(-b-(b^2-4ac))/2a,但若a=0或b^2-4ac<0时,用此公式计算就会出错。从键盘输入a,b,c的值,求x1,x2。如果a=0或b^2-4ac<0,输出错误信息

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    double a,b,c;
    double disc;
    cout<<"Please Enter a,b,c:";
    cin>>a>>b>>c;
    try
    {
        if(a==0)
        {
            throw 0;//
        }
        else
        {
            disc=b*b-4*a*c;
            if(disc<0)
            {
                throw "b*b-4*a*c<0";
            }
             cout<<"x1="<<(-b+sqrt(disc))/2*a<<endl;
             cout<<"x2="<<(-b-sqrt(disc))/2*a<<endl;
        }
    }
    catch(int b)
    {
        cout<<"a="<<b<<endl;
    }
    catch(const char* s)//如果是字符串的话,一定是常量指针变量接受
    {
        cout<<s<<" "<<"This is not fit for a,b,c"<<endl;
    }
    return 0;
}
下面列出程序在3种情况下的运行结果
(1)
Please Enter a,b,c:1 6 2
x1=-0.354249
x2=-5.64575
(2)
Please Enter a,b,c:0 4 5
a=0
(3)
Please Enter a,b,c:2 4 5
b*b-4*a*c<0 This is not fit for a,b,c

(1)首先在try后面的大括号中放置上可能出现异常的语句块或程序段

(2)程序运行时将按正常的顺序执行到try块,执行try块中大括号的语句。如果在执行try块内的语句过程中没有发生异常,则忽略所有的catch块,流程转到catch块后面的语句继续执行。例如,上述代码中结果(1)的情况

3)如果在执行try块内的语句过程中发生异常,则由throw语句抛出一个异常信息。throw抛出什么样的异常取决于自己怎么设计,可以是任何类型的异常。例如,上述代码中抛出的就是字符类型和整形的异常

(4)这个异常信息提供给try-catch结构,系统会查找与之匹配的catch块。若某个catch参数表中的异常声明类型与抛出的异常类型匹配,该catch块就捕获这个异常,执行catch块中的异常处理语句。只要有一个catch捕获捕获了异常,其余的catch块将被忽略。例如,上述代码运行结果的(2)的情况,由try块内的throw语句抛出一个整形异常,被第一个catch捕获,上述代码运行结果的(3)的情况,由try块内的throw语句抛出一个字符串类型异常,被第二个catch捕获

当然,异常类型可以声明为省略号(...),表示可以处理任何类型的异常,注意的是,catch(...)应该放在最后面,如果放在前面,就可以捕获任何异常,那么后面的catch语句块就不会检查和执行了。

(5)在进行异常处理后,程序不会自动终止,继续执行catch块后面的语句

(6)如果throw抛出异常信息找不到与之匹配的catch块,则系统会调用一个系统函数terminate,在屏幕上显示"abnormal praogram termination"。并终止程序的运行

(7)抛出异常信息的throw语句可以与try-catch结构出现在一个函数中,也可以不出现在同一个函数中。这种情况下,当throw抛出异常信息后,首先在本函数中查找与之配对的catch块,如果在本函数中try-catch结构或者没有与之配对的catch块,就转到离开出现异常最近的try-catch结构去处理

例如:

#include<iostream>
#include<cmath>
using namespace std;
double sort_1(double a,double b,double c)
{
    double disc=b*b-4*a*c;
    if(a==0)
    {
        throw a;
    }
    else
    {
        if(disc<0)
        {
            throw "b*b-4*a*c<0";
        }
        return disc;
    }
}
int main()
{
    double a,b,c;
    double disc;
    cout<<"Please Enter a,b,c:";
    cin>>a>>b>>c;
    try
    {
        disc=sort_1(a, b, c);
        cout<<"x1="<<(-b+sqrt(disc))/2*a<<endl;
        cout<<"x2="<<(-b-sqrt(disc))/2*a<<endl;
    }
    catch(int b)
    {
        cout<<"a="<<b<<endl;
    }
    catch(const char* s)//如果是字符串的话,一定是常量指针变量接受
    {
        cout<<s<<" "<<"This is not fit for a,b,c"<<endl;
    }
    return 0;
}

(8)异常处理还可以应用函数嵌套。

例如:

#include<iostream>
using namespace std;
void func2()
{
    double a=0;
    try
    {
        throw a;
    }
    catch(float)
    {
        cout<<"OK2!"<<endl;
    }
    cout<<"end2"<<endl;
}
void func1()
{
    try
    {
        func2();
    }
    catch(double)
    {
        cout<<"OK1!"<<endl;
        throw ;
    }
    cout<<"end1"<<endl;
}
int main()
{
    try
    {
        func1();
    }
    catch(double)
    {
        cout<<"OK0!"<<endl;
    }
    cout<<"end0"<<endl;
    return 0;
}
结果:
OK0!
end0

(2)如果把func2中的catch(float)改为catch(double)
catch(double)
{
    cout<<"OK2!"<<endl;
}
则结果如下:
OK2!
end2
end1
end0

(3)如果把func1中的catch(char)改为catch(double)
catch(double)
{
    cout<<"OK1!"<<endl;
}
则结果如下:
OK1!
end1
end0

(4)在(3)的基础上,cout<<"OK1!"<<endl;后面加了一句throw ;
catch(double)
{
    cout<<"OK1!"<<endl;
    throw ;
}
则结果如下:
OK1!
OK0!
end0

第四种情况与第三种情况不同的是:第四种情况cout<<"OK1!"<<endl;后面加了一句throw ;。在throw语句中可以不包括表达式

例如:

throw ;

此时它将当前正在处理的异常信息再次抛出。再次抛出的异常不会被同一层的catch块捕获,它将被传递给上一层的catch块处理。

修改后的func1函数中的catch块捕获throw抛出的异常信息a,输出"OK1!",但它立即用"throw;"将a再次抛出。被main函数中的catch块捕获,输出"OK0!",执行完毕后继续执行main函数中的catch块后面的语句,输出"end0",程序结束。

注意:只能从catch块再次抛出异常,这中方式有利于构成对同一异常的多层处理机制,使异常能够在恰当的地方被处理,增强异常处理的能力

🌸try的嵌套异常处理语句

在一个try块中可以嵌套另一个try块。每个块都有自己的一组catch块,来处理在try中抛出的异常。try块的catch块只能处理在该try块中抛出的异常。

例如:

try
{
    ...//外层的try语句
    try
    {
        ...//内层的try语句
    }
    catch(elemtype a)//用来捕获内层try中抛出的异常
    {
        ...
    }
    ...
}
catch(elemtype b)//用来捕获外层try中抛出的异常和在内层未捕获到的异常
{
    ...
}
...

上面的语句中,每个try都有一个处理程序,当然,也可以有多个。在内层try块中的代码抛出一个异常时,其处理程序会首先处理它。内层try块的每个处理程序都会检查匹配的异常类型,如果这些处理程序都不匹配,外层try块的处理程序会捕获该异常。

🌸异常与函数

异常处理可以化为一个函数,当每次进行该函数的调用时,异常将被重置。这样编写程序更加简便。

例如:

check是一个检测成绩异常的函数,当成绩达到100分以上或低于60分产生异常,60-100之间为正常成绩

#include<iostream>
using namespace std;
void check(int score)
{
   try
    {
        if(score<60)
        {
            throw score;
        }
        else if(score>100)
        {
            throw score;
        }
        cout<<"分数正常"<<score<<"分"<<endl;
    }
    catch(int s)
    {
        if(s<60)
        {
            cout<<"分数过低"<<s<<"分"<<endl;
        }
        else if(s>100)
        {
            cout<<"分数过高"<<s<<"分"<<endl;
        }
    }
}
int main()
{
    check(12);
    check(21);
    check(109);
    check(99);
    return 0;
}
结果
分数过低12分
分数过低21分
分数过高109分
分数正常99分

🌸异常类

用来传递错异常信息的类就是异常类。异常类可以非常简单,甚至没有任何成员;也可以同普通类一样。

#include<iostream>
using namespace std;
#define MAX 3
class Full
{
    ;
};
class Eempty
{
    ;
};
class Stack
{
public:
    Stack()
    {
        top=0;
    }
    void push(int a);
    int pop();
private:
    int s[MAX];
    int top;
};
void Stack:: push(int a)
{
    if(top>=MAX)
    {
        throw Full();
    }
    s[top]=a;
    top++;
}
int Stack:: pop()
{
    if(top<=0)
    {
        Eempty e1;
        throw e1;
//        throw Eempty();
    }
    return s[--top];
}
int main()
{
    Stack s1;
    try
    {
        s1.push(1);
        s1.push(2);
        s1.push(3);
        s1.push(4);
        cout<<s1.pop()<<endl;
        cout<<s1.pop()<<endl;
        cout<<s1.pop()<<endl;
        cout<<s1.pop()<<endl;
    }
    catch(Full)
    {
        cout<<"Exception: Stack Full!"<<endl;
    }
    catch(Eempty)
    {
        cout<<"Exception: Stack Empty!"<<endl;
    }
    return 0;
}
结果:
Exception: Stack Full!

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸   

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值