异常问题
先说一个大家都知道的现象(既然都知道,好像就没有必要说了,但是为了讨论方便,还是提一下吧,哈哈)。
如果一个局部对象的构造函数没有被成功调用(在执行构造函数时抛出了一个异常),那么在函数“栈展开”时应该不会调用此对象的析构函数,因为C++不会析构“不完整的对象”。如下:
class foo{
public:
int a;
foo() { a = 33; throw 1; }//构造函数中抛出异常
~foo() { cout << a << endl; }
};
void test(){
foo f;
}
void main(int argc, char **argv)
{
try{
test();
}
catch(...){
}
}
程序并不会显示“33”,因为在退出test()执行“栈展开”时并没有调用f的析构函数。
但是如果把foo构造函数中的"throw 1"转移到test函数的结尾处,运行结果就会显示“33”,说明此时编译器在“栈展开”时调用了“完整”对象f的析构函数。
我的问题是:编译器在退出test()执行“栈展开”时怎么知道栈中的f是“不完整的对象”?难道对象本身会带有某些“标记”来标识自己是“不完整”的?或者是通过其他的途径让编译器不去调用f的析构函数?
高手指教,谢谢
问题点数:30、回复次数:9Top
1 楼sharpdew(风刃)回复于 2006-06-02 18:02:58 得分 18
在构造对象的过程中抛出异常,那么也就是说对象没有构造成功,那么也就不存在所谓的对象本身会带有某些“标记”来标识自己是“不完整”的事情了,因为根本没有这个对象。编译器当然是不知道的,这是运行时的事情,也就是说程序执行到抛出异常的这个点上就跳转到别的地方执行了。Top
2 楼sharpdew(风刃)回复于 2006-06-02 18:05:35 得分 0
至于为啥把throw写到test函数的最后,就调用析构函数,那是因为f是局部变量,而且已经构造完整了,当系统在遇到throw并跳出函数之前就会清理此前的局部对象,也就调用了f的析构函数。Top
3 楼OOPhaisky(异化$渴望成功~~)回复于 2006-06-02 18:10:50 得分 0
to sharpdew(风刃) :
按照你的说法,如果f的构造函数成功完成,那么编译器是用什么方法有这么一个对象呢?在符号表中作记录?Top
4 楼OOPhaisky(异化$渴望成功~~)回复于 2006-06-02 18:11:23 得分 0
更正:
to sharpdew(风刃) :
按照你的说法,如果f的构造函数成功完成,那么编译器是用什么方法知道有这么一个对象呢?在符号表中作记录?Top
5 楼cyblueboy83(爱情白痴—电脑迷)回复于 2006-06-02 18:15:17 得分 10
我的理解:
这好像是正常的吧:第一个:foo f;这里构造函数,马上抛出异常,对象没构造成功,何来析构?
第二个,说明对象已经构造成功,只要是合理的编译器都应该做析构
Top
6 楼sharpdew(风刃)回复于 2006-06-02 18:23:47 得分 0
按照你的说法,如果f的构造函数成功完成,那么编译器是用什么方法知道有这么一个对象呢?在符号表中作记录?
~~~~~~~~~~~~~~~
这个问题,你需要问编译器设计者是怎么处理的,我的意思是说所谓不完整是指程序执行时根据抛出异常与否来改变了执行路线,跳过了某些代码。并不是对象真的连地址都没有,毕竟对象是在编译的时候就给分配的空间(动态开辟的除外),而异常处理是在运行时的,这是不同的概念。
Top
7 楼sharpdew(风刃)回复于 2006-06-02 18:26:21 得分 0
对于你上面的程序,如果你把foo f; throw 1;直接写到main函数里面,也不会执行析构函数的,这就是为啥我说代码执行碰到异常就改变路径了。Top
8 楼yuyuxiaoyu(左大S,右小S,偶居中)回复于 2006-06-03 09:44:51 得分 2
继续!Top
9 楼OOPhaisky(异化$渴望成功~~)回复于 2006-06-08 11:57:00 得分 0
我想应该是编译器作了一些“薄记”工作,比如利用某些数据结构标识哪些对象已经构造完成,但是具体的细节还是没有搞清楚。
帖子时间太久了,该结了,来者有分。Top




