C++栈解退

来源:互联网 发布:淘宝天天疯狂购入口 编辑:程序博客网 时间:2024/05/24 06:41

假设try块没有直接调用引发异常的函数,而是调用了对引发异常的函数进行调用的函数,则程序流程将从引发异常的函数跳到包含try块和处理程序的函数。这涉及到栈解退,下面将进行介绍。
首先来看看c++筒仓啊是如何处理函数调用和返回的。c++通常将信息放在栈中来处理函数调用。具体地说,程序将调用函数的指令的地址(返回地址)放在栈中。当被调用的函数执行完毕后,程序将使用该地址来确定从哪里开始继续执行。另外,函数调用,将函数的参数也放在栈中。在栈中,这些函数参数被视为自动变量。如果被调用的函数创建了新的自动变量,这些变量也被添加到了栈中。如果被调用的函数调用了另一个函数,则后者的信息也被添加到栈中,以此类推。
当函数结束后,程序流程将跳到该函数被调用时存储的地址处,同时,栈顶的元素被释放。因此,函数通常都会返回到调用它的函数,以此类推,同时每个函数在结束时都会释放其自动变量。如果自动变量是类对象,则类的析构函数(如果有的话)将被调用。
现在假设函数由于出现异常(而不是由于返回)而终止,则程序也将释放栈中的内存,但不会在释放栈的第一个返回地址后而终止,而是继续释放栈,直到找到第一个位于try块中的返回地址。随后,控制权将转到块尾的异常处理程序,而不是函数调用后的第一条语句,这个过程被称为栈解退。引发机制的一个非常重要的特性是,和函数返回一样,对于栈中的自动类对象,类的析构函数将被调用,然而,函数返回仅仅处理该函数放在栈中的对象,而throw语句则处理try块和throw之间整个函数调用序列放在栈中的对象。
如果没有栈解退这种机制,则引发异常后,对于中间函数调用放在栈中的自动类对象,其构造函数将不会被调用。

虽然throw_catch机制类似于函数参数核函数返回机制,但是还是有些不同之处。其中之一就是函数fun()中的返回语句将控制权返回到调用fun()的函数,但是throw语句将控制权向上返回到第一个这样的函数:
包含能够捕获相应异常try_catch组合。
另一个不同之处是,引发异常时,编译器总是创建一个临时拷贝,即使异常规范和catch块中指定的是引用。例如:
class problem{… };

void super()
{

if(oh_no)
{
problem oops; //construct object
throw oops ; // throw it

}
}

try{
super () ;
}
catch ( problem & p)
{
// statements
}

p 将指向oops的副本,而不是oops本身。这是好事,因为函数super()执行完后,oops将不复存在。顺便说一句,将引发异常和创建对象组合在一起更简单:
throw problem(); //construct and throw default problem object

你可能会问,既然throw语句将生成副本,为何代码中使用引用呢?毕竟,将引用作为返回值的通常原因是避免创建副本以提高效率。答案是,引用还有另一个重要的特征:基类引用可以执行派生类对象。假设有一组通过继承关联起来的异常类型,则在异常规范中只需要列出一个基类引用,它将与任何派生对象匹配。
假设有一个异常类的层次结构,分别要处理不同的异常类型,则使用基类引用将能够任何异常对象;而是用派生类对象只能捕获他所属的类以及从这个类派生而来的类的对象。引发的异常对象将被第一个与之匹配的catch块捕获。这意味着catch块的排列顺序应该与派生顺序相反。

原创粉丝点击