异常 /C++
来源:互联网 发布:淘宝保证金怎么套现 编辑:程序博客网 时间:2024/06/05 13:08
一、C++异常分析:
构造函数中抛出异常是有一定必要的,试想如下情况:
构造函数中有两次new操作,第一次成功了,返回了有效的内存,而第二次失败,此时因为对象构造尚未完成,析构函数是不会调用的,也就是delete语句没有被执行,第一次new出的内存就悬在那儿(发生内存泄漏),所以异常处理程序可以将其暴露出来。
//.....Base(){ int* p = new int(); try{ int* q = new int(); //假如失败 //throw 2... //如果直接抛异常,构造函数失败,不执行析构函数 } catch (...){ delete p; throw; }}
构造函数中遇到异常是不会调用析构函数的,一个对象的父对象的构造函数执行完毕,不能称之为构造完成,对象构造是不可分割的,要么完全成功,要么完全失败,C++保证这一点。对于成员变量,C++遵循这样的规则,即会从异常的发生点按照成员变量的初始化的逆序释放成员。举例来说,有如下初始化列表:
A::A():m1(),m2(),m3(),m4(),m5(){...}
假定m3的初始化过程中抛出异常,则会按照m2,m1的顺序调用这两个成员的析构函数。在{}之间发生的未捕捉异常,最终会导致在栈的开解时析构所有的数据成员。
处理这样的问题,使用智能指针是最好的,这是因为auto_ptr成员是一个对象而不是指针。换句话说,只要不使用原始的指针,那么就不必担心构造函数抛出异常而导致资源泄漏。所以在C++中,资源泄漏的问题一般都用RAII(资源获取就是初始化)的办法:把需要打开/关闭的资源用简单的对象封装起来(这种封装可以同时有多种用处,比如隐藏底层API细节,以利于移植)。
如果不用RAII,即使当前构造函数里获取的东西在析构函数中都释放了,如果某天对类有改动,要新增加一种资源,构造函数里一般能适当地获取,但记不记得在析构函数里相应地释放呢?失误的比率很大。如果考虑到构造函数里抛出异常,就更复杂了。随着项目的不断扩大和时间的推移,这些细节不可能都记得住,而且,有可能会由别人来实施这样的改动。
从运行结果得出以下的结论:
(1)、C++中通知对象构造失败的唯一方法,就是在构造函数中抛出异常;
(2)、对象的部分构造是很常见的,异常的发生点也完全是随机的,程序员要谨慎处理这种情况;
(3)、当对象发生部分构造时,已经构造完成的子对象将会发生逆序地被析构(即异常发生点前面的对象);而还没有开始构建的子对象将不会被 构造了(即异常发生点后面的对象),当然它也就没有析构的过程了;还有正在构建的子对象和对象自己本身就停止继续构建(即出现异常的对象),并且它的析构是不会被执行的。
二、析构函数抛异常的情况:
Effective C++建议,析构函数尽可能地不要抛出异常。设想如果对象出了异常 ,现在异常处理模块为了维护系统数据对象的一致性,避免资源泄漏,有责任释放这个对象的资源,调用对象的析构函数,可现在假如析构过程又再出现异常,那么请问由谁来保证这个对象的资源释放呢?而且新出现的异常由谁来处理?不要忘记前面的一个异常目前都还没有处理结束,因此这就陷入了一个矛盾之中,或者说处于无限的递归嵌套中。
- C异常
- 【c++】异常
- 【C/C++】异常机制
- 异常类型 描述(C#)
- c#中的异常处理
- Exception异常类(C#)
- 捕捉异常 (Visual C#)
- 异常处理 - [C++]
- c异常处理
- Objective-c异常处理
- VS2005(C#)异常解决方案
- object-c 异常
- C语言异常处理
- c 异常处理
- Objective-C中的异常
- C编译异常集锦
- 用C模拟异常
- C 异常处理设计
- $.getJson(url,data,callback)回调函数不执行的问题
- 【笔记】图的基础知识
- 时间戳转换时间
- [牛客网#35D 树的距离]离散化+线段树合并
- nil Nil NULL NSNull 之间的区别
- 异常 /C++
- ROS 学习记录
- [Android]配置Gradle的productFlavors构建项目用于多渠道打包或多环境运行
- 时间戳转几周前
- 如何突破Java程序员三年的门槛
- 论文笔记1:SummaRuNNer: A RNN based Sequence Model for Extractive Summarization of Documents
- Git-储藏(Stashing)
- MFC将ListCtrl列表导出到Excel-采用文件流写Excel兼容Html格式(不需要Excel环境)
- pm2 start 添加参数