您的位置:首页 > 理论基础 > 计算机网络

异常处理 续之(堆栈解退、auto_ptr)http://blog.csdn.net/feitianxuxue/article/details/7314079

2015-10-11 15:31 316 查看

1.堆栈解退

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

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

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

[cpp] view
plaincopy

#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之前发生异常,就不会自动释放该内存:

[cpp] view
plaincopy

void f()

{

int *ip = new int(42);

//如果在new和delete之间发生异常,并且该异常不被局部捕获,就不会执行delete,永远不回收该内存

delete ip;

}

[cpp] view
plaincopy

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)

[cpp] view
plaincopy

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_pro以by value方式被当做一个参数传递给函数,...此时被调用的参数获得了这个auto_pro的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被删除...

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

版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: