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

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值