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!

posted @ 2019-02-26 08:57  Grooovvve  阅读(690)  评论(0编辑  收藏  举报