C++_异常5-异常规范和栈解退
异常规范
异常规范的理念看似有前途,但实际的使用效果并不好。
忽视异常规范之前,您至少应该知道它是什么样的,如下所示:
double harm(double a) throw(bad_thing); //may throw bad_thing exception
double harm(double a) throw(); //doesn't throw an exception
其中throw()部分就是异常规范,它可能出现在函数原型和函数定义中,可包含类型列表,也可不包含。
异常规范的另一个作用是,让编译器添加执行运行阶段检查的代码,检查是否违反了异常规范。
这很难检查;
marm()可能不会引发异常,但它可能调用一个函数,而这个函数调用的另一个函数引发了异常。
另外,您给函数编写代码时它不会引发异常,但库更新后它却会引发异常。
总之,编程社区达成的意见是不要使用这个功能。
栈解退
假设try块没有直接调用引发异常的函数B,而是调用了对引发异常的函数B进行调用的函数A,即A调用了B。
则程序流程将从引发异常的函数B跳到包含try块和处理程序的函数。这涉及到栈解退。
首先要了解C++是如何处理函数调用和返回的。C++通常通过将信息放到栈中来处理函数调用。
具体来说,程序将调用函数的指令和地址(返回地址)放到栈中。
当被调用的函数执行完毕后,程序将使用该地址来决定从哪里开始继续执行。
另外函数调用将函数A参数放到栈中。在栈中这些函数参数被视为自动变量。
如果被调用函数B创建了新的自动变量,则这些变量也将被添加到栈中。
如果被调用函数B调用了另一个函数C,则函数C的信息也将被添加到栈中。以此类推;
当函数C结束时,程序流程将跳到该函数C被调用时存储的地址(返回地址)处,同时栈顶的元素被释放。
因此函数都通常返回到调用它的函数处,同时每个函数都在结束时释放器自动变量。
如果自动变量是类对象,则类的析构函数(如果有的话)也将被调用。
现在假设函数由于出现异常(而不是由于return)而终止,则程序也将释放栈中的内存。
但不会在释放栈的第一个返回地址后停止,而是继续释放栈,直到找到一个位于try块中的返回地址。
随后控制权将转移到位于try块尾的异常处理程序,而不是函数调用后面的第一条语句。
这个过程就被称为栈解退。
和函数返回一样,对于栈中的自动类对象,类的析构函数将被调用。
然而函数返回仅仅处理该函数放在栈中的对象。
而throw语句则处理try块和throw之间整个函数调用序列放在栈中的对象。
接下来上代码,看看栈解退的例子:
1 //error5.cpp -- unwinding the stack 2 #include <iostream> 3 #include <cmath> 4 #include <string> 5 #include "exc_mean.h" 6 7 class demo 8 { 9 private: 10 std::string word; 11 12 public: 13 demo(const std::string & str) 14 { 15 word = str; 16 str::cout<<"demo "<<word<<" created\n"; 17 } 18 19 ~demo() 20 { 21 std::cout<<" demo "<<word<<" destroyed\n"; 22 } 23 24 void show() const 25 { 26 std::cout<<" demo "<<word<< " lives!\n"; 27 } 28 }; 29 30 31 //function prototypes 32 double hmean(double a, double b); 33 double gmean(double a, double b); 34 double means(double a, double b); 35 36 int main() 37 { 38 using std::cout; 39 using std::cin; 40 using std::end; 41 42 double x,y,z; 43 { 44 demo d1("found in block in main()"); 45 cout<<"Enter two numbers:"; 46 while(cin>>x>>y) 47 { 48 try{ 49 z =means(x,y); 50 cout<<"The mean mean of"<<x<<" and "<<y 51 <<" is "<<z<<endl; 52 cout<<"Enter next pair:"; 53 } 54 catch(bad_hmean & bg) 55 { 56 bg.mesg(); 57 cout<<"Try again.\n"; 58 continue; 59 } 60 catch(bad_gmean & hg) 61 { 62 cout<<hg.mesg(); 63 cout<<"Values used:"<<hg.v1<<" , " 64 <<hg.v2<<endl; 65 cout<<"Sorry,you don't get to play any more.\n"; 66 break; 67 } 68 } 69 d1.show(); 70 } 71 cout<<"Bye!\n"; 72 cin.get(); 73 cin.get(); 74 return 0; 75 } 76 77 double hmean(double a, double b) 78 { 79 if(a == -b) 80 throw bad_hmean(a,b); 81 return 2.0*a*b/(a+b); 82 } 83 84 double gmean(double a, double b) 85 { 86 if(a<0||b<0) 87 throw bad_gmean(a,b); 88 return std::sqrt(a*b); 89 } 90 91 double means(double a, double b) 92 { 93 double am, hm, gm; 94 demo d2("found in means()"); 95 am = (a+b)/2.0; 96 try 97 { 98 hm = hmean(a,b); 99 gm = gmean(a,b); 100 101 } 102 catch (bad_hmean & bg) 103 { 104 bg.mesg(); 105 std::cout<<"Caught in means()\n"; 106 throw; //重新抛出异常 107 } 108 d2.show(); 109 return (am+hm+gm)/3.0; 110 111 }
输出结果:
demo found in block in main() created
Enter two numbers: 6 12
demo found in means() created
demo found in means() lives!
demo found in means() destroyed
The mean mean of 6 and 12 is 8.49509
6 -6
demo found in means() created
hmean(6, -6):invalid arguments: a=-b
Caught in means()
demo found in means() destroyed //d2被释放掉了,且来不及调用d2.show()
hmean(6, -6):invalid arguments: a=-b //异常重新抛出到main函数try块后面的catch
Try again. //continue重新开始循环
6 -8
demo found in means() created //d2被创建
demo found in means() destroyed //d2被释放,有异常产生,但是means函数中没有办法处理该异常gmean
gmean() arguments should be >=0 //这个gmean异常被main函数的catch模块捕获并处理
Values used: 6, -8
Sorry, you don't get to play any more. //直接break,结束
demo found in block in main() lives! //d1还在,程序运行到catch后面的语句
demo found in block in main() destroyed //离开了模块,d1被销毁
Bye!