- 浏览: 79175 次
- 性别:
- 来自: 上海
文章分类
构造函数中异常:
1.在无继承关系的前提下,构造函数中抛出异常未尝不可。 因为在对象没能构造完整的情况下,是不会去调用析构函数的。
2.在有继承关系的情况下,构造函数中抛出异常可能会引起问题
2.1如果基类中有纯虚函数,而且在基类的析构函数中有被调用的话,会得到一个 pure virtual function call的异常
2.2如果基类中有纯虚函数,但是基类的析构函数中没有被调用到,是没有关系的。
对2.1的猜想:
因为派生类构造函数在执行的时候,会先去调用基类的构造函数。所以如果异常产生于派生类,系统会认为基类已经构造完毕,但是这时因为派生类还没有构造完毕。又因为此时产生异常,系统需要进行Stack Unwind的操作。会释放认为已经构造完毕的对象。于是,被认为是已经构造完毕的基类对象的析构函数会被系统调用。
猜想一:因为派生对象还没完成构造,导致晚绑定(个人理解为虚函数表未被写入)还未完成。所以只能使用当前可用的调用,即纯虚函数。
猜想二:因为还没构造完派生对象,所以目前的this指针指向的类型,即当前对象类型,仍然被认为是基类的类型,于是。只会调用基类对应的函数。
#ifndef IDISPOSABLE_H #define IDISPOSABLE_H namespace odbclib { class IDisposable { protected: virtual void dispose() = 0; friend class MasterObject; }; } #endif
#ifndef MASTERRESOURCE_H #define MASTERRESOURCE_H #include "odbclib.h" namespace odbclib { class MasterResource :public virtual RelatedResource { public: MasterResource(); virtual ~MasterResource(); protected: typedef stack<SlaveResource*> SlaveStack; void addSlave(SlaveResource &slave); void removeSlave(SlaveResource &slave); virtual void onDisposing(); void disposeSlaves(); private: SlaveStack m_slaves; friend class SlaveResource; }; } #endif
#include "odbclib.h" namespace odbclib { MasterResource::MasterResource(){} MasterResource::~MasterResource() { this->dispose(); } void MasterResource::addSlave(SlaveResource &slave) { m_slaves.push(&slave); } void MasterResource::removeSlave(SlaveResource &slave) { SlaveStack slaves; while(!m_slaves.empty()) { SlaveStack::reference iter = m_slaves.top(); m_slaves.pop(); if(&slave == iter) continue; slaves.push(iter); } while(!slaves.empty()) { m_slaves.push(slaves.top()); slaves.pop(); } } void MasterResource::disposeSlaves() { SlaveStack slaves(m_slaves); while(!slaves.empty()) { SlaveStack::reference slave = slaves.top(); slave->dispose(); slaves.pop(); } } void MasterResource::onDisposing() { this->disposeSlaves(); } }
说明:当MasterObject的disposeSelf是纯虚函数。但是MasterObject的派生类在构造函数中抛出异常,就会导致在Stack unwind期间得到一个pure virtual function call
注意:如果不在派生类的析构函数处显式调用基类的虚函数,而放任由系统调用。则会由于派生类的析构函数已经从虚函数表内移除该虚函数,导致调用的是基类的虚函数。如果此虚函数同样是 纯虚函数,则同样也会得到一个pure virtual function call的异常
=======================================================================
2011-9-23 补充:
=======================================================================
很多资料和帖子上都说 如果 构造函数中抛出异常 ,虽然对象本身是不会被析构,但是对象的成员对象会被析构。
其实这个说法是有非常严重的缺陷的。
class Resource { public: Resource() { cout<<"[Resource]constructing..."<<endl; throw runtime_error("asdfasdfasdf"); } ~Resource() { cout<<"[Resource]destructing..."<<endl; } }; class Ctor { public: Ctor() { cout<<"[Ctor]constructing..."<<endl; } virtual ~Ctor() { cout<<"[Ctor]destructing..."<<endl; } private: Resource m_resource; };
如果main中是这样写的:
int main(int argc,char* argv[])
{
try{
Ctor c;
}
catch(...)
{}
return 0;
}
那么,很幸运的,你可以见识到传说中的stack unwind
但是如果仅仅是下面这样
int main(int argc,char* argv[])
{
Ctor c;
return 0;
}
那就惨了。虽然不知道这样会不会有stack unwind发生。但是很明显的是 c的析构函数是不会被调用到的。
这个问题说大不大,说小不小。
轻者最多是程序退出。然后会完全释放程序所占用的内存,和一些与程序逻辑不相关的资源。
重者数据库连接未断开,事务还没有commit,这时另一个也来了,那就死锁了。很杯具吧!
程序退出的时候很多业务逻辑中重要的资源是不会被程序释放的,虽然这也是情理之中。
但是如此一来。岂不是每次出异常都要加try-catch块,以保证对象的析构函数被调用?
或者每次写main都要来上一个try-catch
As far as I know, C++ standard guarantess that destructors for local variables will be called during the stack unwinding only if the exception is handled (caught and not rethrown). If it is not handled, then the destructors may or may not be called. How do most compilers implement this?
15.5.1p2: "In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate()is called. In all other situations, the stack shall not be unwound before terminate()is called. An implementation is not permitted to finish stack unwinding prematurely based on a determination that the unwind process will eventually cause a call
to terminate()."
I could not verify the quote, hence I am asking for comments.
Roman Werpachowski [Recognized User] Send private email
Thursday, September 27, 2007
Roman Werpachowski [Recognized User] Send private email
Thursday, September 27, 2007
g++ 4.1.2 does not unwind when there is an unhandled exception, according to the test program posted.
Stephen Depooter
Thursday, September 27, 2007
这说明如果没有对异常有catch,那么编译器生成的代码是否会执行stack unwind完全是由编译器自己决定的。
并且已经有好心人测试过几个版本的g++
编译器 | exception-unhandle 时是否会 stack unwind |
g++ version 2.96 20000731 (Red Hat Linux 7.3 2.96-110) | 是 |
g++ version 3.3.6 (PLD Linux) | 否 |
g++ 4.1.2 | 否 |
g++ 4.5.2 | 否 |
vc++ 2010 | 否 |
发表评论
-
pthread_rwlock_t 未定义的问题
2016-08-06 15:02 1463最近在linux上次编译时出现pthread_rwlock ... -
二级指针 const 参数
2013-01-11 22:33 2028http://www.parashift.com/c++-fa ... -
tricks
2012-12-31 14:21 765#include <typeinfo> #inc ... -
多输出带前缀输出流
2012-10-29 11:37 987http://stackoverflow.com/questi ... -
inherit the ostream
2012-10-26 16:02 848class cdebug_stream :public ost ... -
模板参数 函数指针
2011-12-22 13:12 2390http://stackoverflow.com/questi ... -
event
2011-08-19 14:42 773#ifndef ODBCLIB_CORE_EVENT_EVEN ... -
c++ Delegate
2011-08-06 19:59 1584#ifndef DUMMYCLASS_H #defin ... -
Nullable (bug)
2011-06-26 23:45 952#ifndef NULLABLE_H #define ... -
C++ 接异常时的注意
2011-06-24 23:55 844void test1() { try { ... -
类成员函数模板特化 默认参数
2011-06-19 11:23 2469#ifndef MEMORYBLOCK_H #defi ... -
类成员函数模板特化
2011-06-12 20:20 2847//header file namespace odb ...
相关推荐
4. **主函数**:在`main()`函数中,首先创建一个栈`S`,然后进行一系列的栈操作,包括入栈、遍历、弹栈和清栈,展示了栈的基本功能。 总结来说,这段代码是关于链表实现的栈数据结构,涉及到链表节点的定义、栈...
数据结构中的栈是一种非常基础且重要的抽象数据类型,它的特点是具有后进先出(LIFO,Last In First Out)的特性。栈的操作主要包括入栈(Push)和出栈(Pop)。在C语言中,我们可以使用两种方式来实现栈:静态栈...
“汇兑风险、清关和清税” 本资源摘要信息主要讲解了汇兑风险、清关和清税三个方面的知识点。下面将详细介绍每个方面的知识点。 一、汇兑风险 汇兑风险是指在国际工程项目中,因汇率变化而产生的风险。这种风险...
中石化加油卡开户和清户顺序.docx
栈的实现还需要包括栈的操作函数,例如初始化栈、销毁栈、进栈、出栈、判断栈空和清空栈等。 栈的应用:栈的应用非常广泛,例如在编译器设计、计算机网络协议和操作系统中都有栈的应用。栈的应用还可以用于解决递归...
现金收付流程和清分整点规范方案.doc
税控盘,金税盘,税务UKey抄税和清卡接口
通过识别和分类不同的呼吸行为,如咳嗽和清嗓,医生和康复专家可以获得关于患者呼吸状况的即时反馈,进而提供更加个性化和精准的治疗方案。 在这项研究中,研究人员通过对26名健康个体在不同体位下的咳嗽和清嗓声音...
证券支付和清结算流程涉及到多个金融机构和复杂的操作环节,对于理解和参与证券市场的投资者至关重要。本文主要探讨证券支付清结算的基本概念、参与者以及流程。 首先,证券账户是投资者进行证券交易的基础,分为...
《ETC费显和清分结算系统优化工程ETCMTC混合车道系统实时指南》是针对2020年1月1日取消高速公路省界收费站工程并网切换后出现的一系列问题而制定的技术实施方案。在全网系统切换至分段式计费方式后,由于各省份系统...
- 开发者可以使用`FLAG_ACTIVITY_NEW_TASK`和`FLAG_ACTIVITY_CLEAR_TOP`等标志来控制Activity如何入栈和清栈。 7. **源码分析** - 分析源码可以帮助我们理解AMS如何调度Activity,如何决定进程的生死,以及如何...
此外,课程还要求学生访问网络学堂和清橙考试系统,以便查阅作业文件,以及参考《C++语言程序设计(第4版学生用书)》来完成实验内容。这些安排都旨在提供全方位的学习资源,帮助学生更好地理解和掌握C++编程的知识...
《宿新市徐公店》《四时田园杂兴(其二十五)》《清平乐_村居》教学反思2.pdf
数据库清理和清档是数据库管理中的重要环节,用于优化存储空间、提高系统性能以及保障数据安全。本资源提供的是一个名为“数据库清理清档工具v1版本”的实用工具,由KCLA_sweettld开发,旨在帮助用户快速、便捷地...
为满足交通运输部在取消高速公路省界收费站不停车电子收费系统费显和清分结算系统优化试点工程中对ETC通行车辆分段计费和计费结果的要求,重点阐述门架元整计费,固定折扣率出口元整计费和差异化折扣率出口元整计费等3...
RPA技术最早被引入财务领域,研发出的财务机器人开始逐渐替代一些重复性高、规则性强的财务工作。财务机器人能够高效地执行数据录入、对账、报表生成等工作,大幅度提高工作效率。然而,RPA技术在财务领域中的应用也...
血常规和肝肾功能是医学检验中的重要项目,它们能够反映出人体内许多生理和病理状况。这份“化验单判读”主要涉及血清蛋白、肝功能、肾功能以及胆红素等方面的指标,下面将详细解释这些指标的临床意义。 1. 血清总...
1. **血清总蛋白和清蛋白**: - 血清总蛋白由清蛋白和球蛋白组成,主要反映肝脏的合成功能。正常值通常在60-80g/L之间。 - 清蛋白(白蛋白)降低可能表示肝功能障碍、营养不良或肾病。若球蛋白比例相对增加,可能...
这样的设计使得机器人可以在不停电的情况下进行巡检和清障工作,有效避免了人工上杆操作的危险,减少了线路损伤和断裂的风险,从而提高了运行检修效率并降低了因拉闸停电造成的经济损失和社会效益不良影响。...
1. **KDBook.Ani**:可能是一个动态图库,用于展示反过账和清日志的动画教程,帮助用户直观理解操作流程。 2. **KDAppSvr.dll**:这是一个动态链接库文件,可能为金蝶系统的一部分,提供反过账和清日志功能的底层...