`
chenqi210
  • 浏览: 76698 次
  • 性别: 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

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics