- 浏览: 78688 次
- 性别:
- 来自: 上海
文章分类
构造函数中异常:
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 1435最近在linux上次编译时出现pthread_rwlock ... -
二级指针 const 参数
2013-01-11 22:33 2020http://www.parashift.com/c++-fa ... -
tricks
2012-12-31 14:21 750#include <typeinfo> #inc ... -
多输出带前缀输出流
2012-10-29 11:37 980http://stackoverflow.com/questi ... -
inherit the ostream
2012-10-26 16:02 835class cdebug_stream :public ost ... -
模板参数 函数指针
2011-12-22 13:12 2377http://stackoverflow.com/questi ... -
event
2011-08-19 14:42 770#ifndef ODBCLIB_CORE_EVENT_EVEN ... -
c++ Delegate
2011-08-06 19:59 1576#ifndef DUMMYCLASS_H #defin ... -
Nullable (bug)
2011-06-26 23:45 942#ifndef NULLABLE_H #define ... -
C++ 接异常时的注意
2011-06-24 23:55 837void test1() { try { ... -
类成员函数模板特化 默认参数
2011-06-19 11:23 2458#ifndef MEMORYBLOCK_H #defi ... -
类成员函数模板特化
2011-06-12 20:20 2838//header file namespace odb ...
相关推荐
4. **主函数**:在`main()`函数中,首先创建一个栈`S`,然后进行一系列的栈操作,包括入栈、遍历、弹栈和清栈,展示了栈的基本功能。 总结来说,这段代码是关于链表实现的栈数据结构,涉及到链表节点的定义、栈...
数据结构中的栈是一种非常基础且重要的抽象数据类型,它的特点是具有后进先出(LIFO,Last In First Out)的特性。栈的操作主要包括入栈(Push)和出栈(Pop)。在C语言中,我们可以使用两种方式来实现栈:静态栈...
“汇兑风险、清关和清税” 本资源摘要信息主要讲解了汇兑风险、清关和清税三个方面的知识点。下面将详细介绍每个方面的知识点。 一、汇兑风险 汇兑风险是指在国际工程项目中,因汇率变化而产生的风险。这种风险...
中石化加油卡开户和清户顺序.docx
栈的实现还需要包括栈的操作函数,例如初始化栈、销毁栈、进栈、出栈、判断栈空和清空栈等。 栈的应用:栈的应用非常广泛,例如在编译器设计、计算机网络协议和操作系统中都有栈的应用。栈的应用还可以用于解决递归...
现金收付流程和清分整点规范方案.doc
在这篇研究中,作者旨在通过声学分析和机器学习技术来构建一个能区分咳嗽和清嗓行为的分类模型,这在肺康复领域具有重要意义。他们选取了26名健康的参与者(男性11人,女性15人),让他们在不同的体位(平卧位、45°...
税控盘,金税盘,税务UKey抄税和清卡接口
证券支付和清结算流程涉及到多个金融机构和复杂的操作环节,对于理解和参与证券市场的投资者至关重要。本文主要探讨证券支付清结算的基本概念、参与者以及流程。 首先,证券账户是投资者进行证券交易的基础,分为...
《ETC费显和清分结算系统优化工程ETCMTC混合车道系统实时指南》是针对2020年1月1日取消高速公路省界收费站工程并网切换后出现的一系列问题而制定的技术实施方案。在全网系统切换至分段式计费方式后,由于各省份系统...
- 开发者可以使用`FLAG_ACTIVITY_NEW_TASK`和`FLAG_ACTIVITY_CLEAR_TOP`等标志来控制Activity如何入栈和清栈。 7. **源码分析** - 分析源码可以帮助我们理解AMS如何调度Activity,如何决定进程的生死,以及如何...
血清总蛋白和清蛋白的异常变化,特别是持续降低,可能预示着肝病的进展和预后的不良。而血清总蛋白和球蛋白的增高,尤其是γ球蛋白的增多,可能提示慢性肝病、恶性肿瘤或多发性骨髓瘤等疾病。 血清α1-抗胰蛋白酶...
此外,课程还要求学生访问网络学堂和清橙考试系统,以便查阅作业文件,以及参考《C++语言程序设计(第4版学生用书)》来完成实验内容。这些安排都旨在提供全方位的学习资源,帮助学生更好地理解和掌握C++编程的知识...
《宿新市徐公店》《四时田园杂兴(其二十五)》《清平乐_村居》教学反思2.pdf
数据库清理和清档是数据库管理中的重要环节,用于优化存储空间、提高系统性能以及保障数据安全。本资源提供的是一个名为“数据库清理清档工具v1版本”的实用工具,由KCLA_sweettld开发,旨在帮助用户快速、便捷地...
为满足交通运输部在取消高速公路省界收费站不停车电子收费系统费显和清分结算系统优化试点工程中对ETC通行车辆分段计费和计费结果的要求,重点阐述门架元整计费,固定折扣率出口元整计费和差异化折扣率出口元整计费等3...
此时,确保乘客安全撤离优先,系统会立即开启所有出站检票机的扇门,允许乘客无需检票快速离开。设备将显示紧急状态信息,半自动售票机显示紧急提示,自动售票机暂停服务,检票机上的乘客显示器显示紧急信息,导向...
RPA技术最早被引入财务领域,研发出的财务机器人开始逐渐替代一些重复性高、规则性强的财务工作。财务机器人能够高效地执行数据录入、对账、报表生成等工作,大幅度提高工作效率。然而,RPA技术在财务领域中的应用也...
血常规和肝肾功能是医学检验中的重要项目,它们能够反映出人体内许多生理和病理状况。这份“化验单判读”主要涉及血清蛋白、肝功能、肾功能以及胆红素等方面的指标,下面将详细解释这些指标的临床意义。 1. 血清总...
1. **血清总蛋白和清蛋白**: - 血清总蛋白由清蛋白和球蛋白组成,主要反映肝脏的合成功能。正常值通常在60-80g/L之间。 - 清蛋白(白蛋白)降低可能表示肝功能障碍、营养不良或肾病。若球蛋白比例相对增加,可能...