异常处理 续之(堆栈解退、auto_ptr)

来源:互联网 发布:卖家设置淘宝客 编辑:程序博客网 时间:2024/06/01 13:15

1.堆栈解退

当抛出了异常,但还没在特定的作用域中被捕获时,函数调用堆栈便被“解退”,并试图在下一个外层try...catch代码中捕获这个异常。解退函数调用堆栈意味着抛出未捕获异常的那个函数将终止,这个函数中的所有局部变量都将销毁,控制会返回到原先调用这个函数的语句。

如果有一个try代码块包含了这条语句,则它就会试图捕获这个异常。如果没有代码块包含这条语句,则堆栈解退再次发生。如果没有任何catch处理器捕获这个异常,则会调用terminate函数,终止程序。

下面的demo演示了堆栈解退:

#include <iostream>#include <stdexcept>using namespace std;void fun3() throw (runtime_error){ cout<<"In fun 3"<<endl; throw runtime_error("runtime_error in fun3");}void fun2() throw (runtime_error){ cout<<"fun3 is called inside fun2"<<endl; fun3();}void fun1() throw (runtime_error){ cout<<"fun2 is called inside fun1"<<endl; fun2();}int _tmain(int argc, _TCHAR* argv[]){ try {  cout<<"fun1 is called inside main"<<endl;   fun1(); } catch(runtime_error &error) {  cout<<"Exception occurred: "<< error.what()<<endl;  cout<<"exception handled in main"<<endl; } system("pause"); return 0;}

运行结果:

2.构造函数、析构函数与异常处理

探讨一个问题:如果在构造函数中检测到错误,会发生什么事情?

例如:当new运算符用于无法分配所需的内存来保存对象的内部表示而失败时,这个对象的构造函数该如何响应?

在构造函数抛出异常前,会调用作为这个对象的一部分而构建的任何成员对象的析构函数。在抛出异常前,会调用try代码块中构建的每一个自动对象的析构函数。当异常处理器开始执行的时刻,会保证已经完成了堆栈解退。如果作为堆栈解退的结果而被调用的析构函数抛出了异常,则会调用terminate函数。

如果对象具有成员对象,并且如果外层对象被完全构建之前就抛出了异常,则在异常发生之前,会为已经构建的成员对象执行析构函数。

异常可能会阻止释放资源的代码的执行,从而导致内存泄露。解决这一问题的一种技术是:初始化一个局部对象,以获取这个资源。当异常发生时,就会调用这个对象的析构函数,并可释放资源。

3.Auto+ptr类与动态内存分配

auto_ptr类是一个接受一个类型参数的模板,它为动态分配对象提供了异常安全。

动态内存分配,就是将这块内存的地址赋予指针,通过这个指针来操作这块内存。当内存不在需要时,使用delete运算符来清理这块内存。

如果在内存分配成功之后,delete语句执行之前发生了异常,就可能导致内存泄露。C++标准库在头文件<memory>中提供了类模板auto_ptr,用于处理这种情况。

auto_ptr类的对象维护着指向动态分配内存的指针。当调用auto_ptr对象的析构函数时(当auto_ptr对象超出作用域范围时,就会调用析构函数),它对指针数据成员执行delete操作。

auto_ptr对象只能保存一个指向对象的指针,并不能用于指向动态分配的数组,使用auto_ptr对象指向动态分配的数组会导致未定义的运行时行为。

为异常安全的内存分配使用auto_ptr

如果通过常规指针分配内存,而且在执行delete之前发生异常,就不会自动释放该内存:

void f(){ int *ip = new int(42); //如果在new和delete之间发生异常,并且该异常不被局部捕获,就不会执行delete,永远不回收该内存 delete ip;}
void f(){ auto_ptr<int> ap(new int(42)); //编译器保证在展开栈越过f之前运行ap的析构函数}

下面是一个面试题,我在CSDN论坛上看到的:

来自(http://topic.csdn.net/u/20120229/20/d5442409-f8c5-45a2-90d7-6c746859ef4a.html

template <typename T> //auto_ptr是智能指针,所有权转移void Func2(std::auto_ptr<T> v1, std::auto_ptr<T> v2, std::auto_ptr<T> v3){ <…>}template <typename T> bool Func1(){ try {  std::auto_ptr<T> p(new T);  Func2(p, std::auto_ptr<T> (new T), std::auto_ptr<T>(new T));  //调用Func2后,P就是NULL了  p->SomeMethod();  //出错了  return true; } catch(const std::bad_alloc&) {  return false; }}

问题:

Do you see any problems in this code?
Is this code an exception safe?
If you see any problems please mark all of them (by using the following style: line of code – description of the problem; for example: “printf(s) – uninitialized variable s”).

在论坛上看到一句话:

<<C++标准程序库>>42页第一自然段:...如果auto_proby value方式被当做一个参数传递给函数,...此时被调用的参数获得了这个auto_pro的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被删除...

 

 

参考资料:《c++程序员教程》 电子工业出版社 PP463-P470  《c++ primer 中文版 第4版》 人民邮电出版社  P580-596

原创粉丝点击