C++异常处理的实现

简介: 🐰C++异常处理的实现🌸try的嵌套异常处理语句🌸异常与函数🌸异常类

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

目录

🐰C++异常处理的实现

🌸try的嵌套异常处理语句

🌸异常与函数

🌸异常类


🐰C++异常处理的实现

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

例如:

1. try
2. {
3.     ...
4.     if(表达式1)
5.         throw x1;
6.     ...
7.     if(表达式2)
8.         throw x1;
9.     ...
10.     if(表达式n)
11.         throw xn;
12.     ...
13. }
14. catch(异常类型声明1)
15. {
16.     异常类型语句序列1
17. }
18. catch(异常类型声明2)
19. {
20.     异常类型语句序列2
21. }
22. ...
23. catch(异常类型声明n)
24. {
25.     异常类型语句序列n
26. }

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

例如:

1. throw语句的语法格式
2. 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参数表中写出变量名。

例如:

1. catch(double d)
2. {
3.     cout<<"throw "<<d;
4. }
5. 这时输出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,输出错误信息

1. #include<iostream>
2. #include<cmath>
3. using namespace std;
4. int main()
5. {
6.     double a,b,c;
7.     double disc;
8.     cout<<"Please Enter a,b,c:";
9.     cin>>a>>b>>c;
10.     try
11.     {
12.         if(a==0)
13.         {
14.             throw 0;//
15.         }
16.         else
17.         {
18.             disc=b*b-4*a*c;
19.             if(disc<0)
20.             {
21.                 throw "b*b-4*a*c<0";
22.             }
23.              cout<<"x1="<<(-b+sqrt(disc))/2*a<<endl;
24.              cout<<"x2="<<(-b-sqrt(disc))/2*a<<endl;
25.         }
26.     }
27.     catch(int b)
28.     {
29.         cout<<"a="<<b<<endl;
30.     }
31.     catch(const char* s)//如果是字符串的话,一定是常量指针变量接受
32.     {
33.         cout<<s<<" "<<"This is not fit for a,b,c"<<endl;
34.     }
35.     return 0;
36. }
37. 下面列出程序在3种情况下的运行结果
38. (1)
39. Please Enter a,b,c:1 6 2
40. x1=-0.354249
41. x2=-5.64575
42. (2)
43. Please Enter a,b,c:0 4 5
44. a=0
45. (3)
46. Please Enter a,b,c:2 4 5
47. 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结构去处理

例如:

1. #include<iostream>
2. #include<cmath>
3. using namespace std;
4. double sort_1(double a,double b,double c)
5. {
6.     double disc=b*b-4*a*c;
7.     if(a==0)
8.     {
9.         throw a;
10.     }
11.     else
12.     {
13.         if(disc<0)
14.         {
15.             throw "b*b-4*a*c<0";
16.         }
17.         return disc;
18.     }
19. }
20. int main()
21. {
22.     double a,b,c;
23.     double disc;
24.     cout<<"Please Enter a,b,c:";
25.     cin>>a>>b>>c;
26.     try
27.     {
28.         disc=sort_1(a, b, c);
29.         cout<<"x1="<<(-b+sqrt(disc))/2*a<<endl;
30.         cout<<"x2="<<(-b-sqrt(disc))/2*a<<endl;
31.     }
32.     catch(int b)
33.     {
34.         cout<<"a="<<b<<endl;
35.     }
36.     catch(const char* s)//如果是字符串的话,一定是常量指针变量接受
37.     {
38.         cout<<s<<" "<<"This is not fit for a,b,c"<<endl;
39.     }
40.     return 0;
41. }

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

例如:

1. #include<iostream>
2. using namespace std;
3. void func2()
4. {
5.     double a=0;
6.     try
7.     {
8.         throw a;
9.     }
10.     catch(float)
11.     {
12.         cout<<"OK2!"<<endl;
13.     }
14.     cout<<"end2"<<endl;
15. }
16. void func1()
17. {
18.     try
19.     {
20.         func2();
21.     }
22.     catch(double)
23.     {
24.         cout<<"OK1!"<<endl;
25.         throw ;
26.     }
27.     cout<<"end1"<<endl;
28. }
29. int main()
30. {
31.     try
32.     {
33.         func1();
34.     }
35.     catch(double)
36.     {
37.         cout<<"OK0!"<<endl;
38.     }
39.     cout<<"end0"<<endl;
40.     return 0;
41. }
42. 结果:
43. OK0!
44. end0
45. 
46. (2)如果把func2中的catch(float)改为catch(double)
47. catch(double)
48. {
49.     cout<<"OK2!"<<endl;
50. }
51. 则结果如下:
52. OK2!
53. end2
54. end1
55. end0
56. 
57. (3)如果把func1中的catch(char)改为catch(double)
58. catch(double)
59. {
60.     cout<<"OK1!"<<endl;
61. }
62. 则结果如下:
63. OK1!
64. end1
65. end0
66. 
67. (4)在(3)的基础上,cout<<"OK1!"<<endl;后面加了一句throw ;
68. catch(double)
69. {
70.     cout<<"OK1!"<<endl;
71.     throw ;
72. }
73. 则结果如下:
74. OK1!
75. OK0!
76. 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块中抛出的异常。

例如:

1. try
2. {
3.     ...//外层的try语句
4.     try
5.     {
6.         ...//内层的try语句
7.     }
8.     catch(elemtype a)//用来捕获内层try中抛出的异常
9.     {
10.         ...
11.     }
12.     ...
13. }
14. catch(elemtype b)//用来捕获外层try中抛出的异常和在内层未捕获到的异常
15. {
16.     ...
17. }
18. ...

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

🌸异常与函数

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

例如:

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

1. #include<iostream>
2. using namespace std;
3. void check(int score)
4. {
5.    try
6.     {
7.         if(score<60)
8.         {
9.             throw score;
10.         }
11.         else if(score>100)
12.         {
13.             throw score;
14.         }
15.         cout<<"分数正常"<<score<<"分"<<endl;
16.     }
17.     catch(int s)
18.     {
19.         if(s<60)
20.         {
21.             cout<<"分数过低"<<s<<"分"<<endl;
22.         }
23.         else if(s>100)
24.         {
25.             cout<<"分数过高"<<s<<"分"<<endl;
26.         }
27.     }
28. }
29. int main()
30. {
31.     check(12);
32.     check(21);
33.     check(109);
34.     check(99);
35.     return 0;
36. }
37. 结果
38. 分数过低12分
39. 分数过低21分
40. 分数过高109分
41. 分数正常99分

🌸异常类

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

1. #include<iostream>
2. using namespace std;
3. #define MAX 3
4. class Full
5. {
6.     ;
7. };
8. class Eempty
9. {
10.     ;
11. };
12. class Stack
13. {
14. public:
15.     Stack()
16.     {
17.         top=0;
18.     }
19.     void push(int a);
20.     int pop();
21. private:
22.     int s[MAX];
23.     int top;
24. };
25. void Stack:: push(int a)
26. {
27.     if(top>=MAX)
28.     {
29.         throw Full();
30.     }
31.     s[top]=a;
32.     top++;
33. }
34. int Stack:: pop()
35. {
36.     if(top<=0)
37.     {
38.         Eempty e1;
39.         throw e1;
40. //        throw Eempty();
41.     }
42.     return s[--top];
43. }
44. int main()
45. {
46.     Stack s1;
47.     try
48.     {
49.         s1.push(1);
50.         s1.push(2);
51.         s1.push(3);
52.         s1.push(4);
53.         cout<<s1.pop()<<endl;
54.         cout<<s1.pop()<<endl;
55.         cout<<s1.pop()<<endl;
56.         cout<<s1.pop()<<endl;
57.     }
58.     catch(Full)
59.     {
60.         cout<<"Exception: Stack Full!"<<endl;
61.     }
62.     catch(Eempty)
63.     {
64.         cout<<"Exception: Stack Empty!"<<endl;
65.     }
66.     return 0;
67. }
68. 结果:
69. Exception: Stack Full!

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



相关文章
|
3月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
74 2
|
6月前
|
安全 编译器 程序员
【C++ 泛型编程 进阶篇】 C++ 泛型编程 模板与异常处理、模板与友元之间的使用(一)
【C++ 泛型编程 进阶篇】 C++ 泛型编程 模板与异常处理、模板与友元之间的使用
92 1
|
6月前
|
安全 C++
C++中的异常处理与错误处理机制
C++中的异常处理与错误处理机制
73 0
|
5月前
|
C++
C++一分钟之—异常处理try-catch
【6月更文挑战第22天】C++异常处理机制,借助`try`、`catch`、`throw`管理错误,优雅处理异常,防止程序崩溃。`try`包围可能出错的代码,`catch`捕获异常,`throw`引发异常。基本结构是:`try-catch`块中,未捕获的异常将向上抛出。多`catch`块可按顺序捕获不同类型的异常。易错点包括忽视异常传播、不精确的`catch`和资源未清理。通过精确捕获、RAII技术和适当的异常策略,提升代码健壮性和效率。
41 1
|
5月前
|
C++
C++核心技术要点《异常处理详解》
C++核心技术要点《try-throw-catch异常处理详解》
51 2
|
5月前
|
程序员 编译器 C++
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
47 0
|
6月前
|
程序员 编译器 C++
C++中的异常处理:技术详解与实践
C++中的异常处理:技术详解与实践
112 1
|
5月前
|
C++
C++对C的改进和拓展\异常处理
C++对C的改进和拓展\异常处理
35 0
|
6月前
|
C++
C++程序异常处理
C++程序异常处理
41 1
|
5月前
|
C++
Essential C++ 第7章 异常处理
Essential C++ 第7章 异常处理