🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
🐰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!
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸