`
chenqi210
  • 浏览: 79175 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

关于抛出异常和清栈

    博客分类:
  • c++
阅读更多

构造函数中异常:

 

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

 

http://discuss.joelonsoftware.com/default.asp?joel.3.546675.19 写道
How C++ compilers implement stack unwinding?
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?

 

http://discuss.joelonsoftware.com/default.asp?joel.3.546675.19 写道
This is supposed to be a quote from the C++ standard:

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
 

 

http://discuss.joelonsoftware.com/default.asp?joel.3.546675.19 写道
On one machine I have access to, g++ version 2.96 20000731 (Red Hat Linux 7.3 2.96-110) unwinds the stack even if there is no handler. On another, g++ version 3.3.6 (PLD Linux) does not. I wonder how g++ v4 works.
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

 

分享到:
评论

相关推荐

    代码171491308程祥吉1

    4. **主函数**:在`main()`函数中,首先创建一个栈`S`,然后进行一系列的栈操作,包括入栈、遍历、弹栈和清栈,展示了栈的基本功能。 总结来说,这段代码是关于链表实现的栈数据结构,涉及到链表节点的定义、栈...

    C语言 数据结构中栈的实现代码

    数据结构中的栈是一种非常基础且重要的抽象数据类型,它的特点是具有后进先出(LIFO,Last In First Out)的特性。栈的操作主要包括入栈(Push)和出栈(Pop)。在C语言中,我们可以使用两种方式来实现栈:静态栈...

    汇兑风险、清关和清税.pptx

    “汇兑风险、清关和清税” 本资源摘要信息主要讲解了汇兑风险、清关和清税三个方面的知识点。下面将详细介绍每个方面的知识点。 一、汇兑风险 汇兑风险是指在国际工程项目中,因汇率变化而产生的风险。这种风险...

    中石化加油卡开户和清户顺序.docx

    中石化加油卡开户和清户顺序.docx

    浅析顺序结构存储的栈

    栈的实现还需要包括栈的操作函数,例如初始化栈、销毁栈、进栈、出栈、判断栈空和清空栈等。 栈的应用:栈的应用非常广泛,例如在编译器设计、计算机网络协议和操作系统中都有栈的应用。栈的应用还可以用于解决递归...

    现金收付流程和清分整点规范方案.doc

    现金收付流程和清分整点规范方案.doc

    税控盘,金税盘,税务UKey抄税和清卡接口

    税控盘,金税盘,税务UKey抄税和清卡接口

    基于声学分析和机器学习构建咳嗽和清嗓分类模型.pdf

    通过识别和分类不同的呼吸行为,如咳嗽和清嗓,医生和康复专家可以获得关于患者呼吸状况的即时反馈,进而提供更加个性化和精准的治疗方案。 在这项研究中,研究人员通过对26名健康个体在不同体位下的咳嗽和清嗓声音...

    证券支付(银行转证券、证券转银行)和清结算的流程.docx

    证券支付和清结算流程涉及到多个金融机构和复杂的操作环节,对于理解和参与证券市场的投资者至关重要。本文主要探讨证券支付清结算的基本概念、参与者以及流程。 首先,证券账户是投资者进行证券交易的基础,分为...

    ETC费显和清分结算系统优化工程ETCMTC混合车道系统实时指南 .pdf

    《ETC费显和清分结算系统优化工程ETCMTC混合车道系统实时指南》是针对2020年1月1日取消高速公路省界收费站工程并网切换后出现的一系列问题而制定的技术实施方案。在全网系统切换至分段式计费方式后,由于各省份系统...

    Android任务管理器源码.zip

    - 开发者可以使用`FLAG_ACTIVITY_NEW_TASK`和`FLAG_ACTIVITY_CLEAR_TOP`等标志来控制Activity如何入栈和清栈。 7. **源码分析** - 分析源码可以帮助我们理解AMS如何调度Activity,如何决定进程的生死,以及如何...

    面向对象程序设计(C++)课程教学日历.pdf

    此外,课程还要求学生访问网络学堂和清橙考试系统,以便查阅作业文件,以及参考《C++语言程序设计(第4版学生用书)》来完成实验内容。这些安排都旨在提供全方位的学习资源,帮助学生更好地理解和掌握C++编程的知识...

    《宿新市徐公店》《四时田园杂兴(其二十五)》《清平乐_村居》教学反思2.pdf

    《宿新市徐公店》《四时田园杂兴(其二十五)》《清平乐_村居》教学反思2.pdf

    数据库清理清档工具v1版本.rar_KCLA_sweettld_数据库清理工具_清档

    数据库清理和清档是数据库管理中的重要环节,用于优化存储空间、提高系统性能以及保障数据安全。本资源提供的是一个名为“数据库清理清档工具v1版本”的实用工具,由KCLA_sweettld开发,旨在帮助用户快速、便捷地...

    高速公路ETC通行车辆门架分段计费解决方案

    为满足交通运输部在取消高速公路省界收费站不停车电子收费系统费显和清分结算系统优化试点工程中对ETC通行车辆分段计费和计费结果的要求,重点阐述门架元整计费,固定折扣率出口元整计费和差异化折扣率出口元整计费等3...

    RPA技术自动化运维的研究及应用.pdf

    RPA技术最早被引入财务领域,研发出的财务机器人开始逐渐替代一些重复性高、规则性强的财务工作。财务机器人能够高效地执行数据录入、对账、报表生成等工作,大幅度提高工作效率。然而,RPA技术在财务领域中的应用也...

    化验单判读(血常规、肝肾功)[整理].pdf

    血常规和肝肾功能是医学检验中的重要项目,它们能够反映出人体内许多生理和病理状况。这份“化验单判读”主要涉及血清蛋白、肝功能、肾功能以及胆红素等方面的指标,下面将详细解释这些指标的临床意义。 1. 血清总...

    化验单判读(血常规、肝肾功).doc

    1. **血清总蛋白和清蛋白**: - 血清总蛋白由清蛋白和球蛋白组成,主要反映肝脏的合成功能。正常值通常在60-80g/L之间。 - 清蛋白(白蛋白)降低可能表示肝功能障碍、营养不良或肾病。若球蛋白比例相对增加,可能...

    输电线路带电作业巡检清障机器人的设计.pdf

    这样的设计使得机器人可以在不停电的情况下进行巡检和清障工作,有效避免了人工上杆操作的危险,减少了线路损伤和断裂的风险,从而提高了运行检修效率并降低了因拉闸停电造成的经济损失和社会效益不良影响。...

    反过账,清日志工具3.0 4.0 4.2通用(支持反过账调整期间凭证).zip

    1. **KDBook.Ani**:可能是一个动态图库,用于展示反过账和清日志的动画教程,帮助用户直观理解操作流程。 2. **KDAppSvr.dll**:这是一个动态链接库文件,可能为金蝶系统的一部分,提供反过账和清日志功能的底层...

Global site tag (gtag.js) - Google Analytics