案例1
这是基于昨天发布的另一个question of mine,我接受了,尽管不情愿,可以通过以下代码进行总结:
{
...
goto Label;
A a;
...
Label:DoSomething();
}
其中A
是具有已定义constructor
和默认destructor
的任何类。 DoSomething()
没有使用a
。根据我的理解(我仍在努力使用C ++基本结构)A
在这种情况下不会调用析构函数,因为对象a
甚至没有被构造。
请注意,VS2008编译器会向您发出警告(C4533 : initialization of 'a' is skipped by 'goto Label'
)而不是错误,除非您尝试为destructor
定义A
,当编译器神秘地更改警告时陷入错误(C2362 : initialization of 'a' is skipped by 'goto Label'
),好像阻止某人探究问题,顺便提一下,这让我想起了自然中的量子现象。
案例2
这是constructor
抛出异常的正常现象,在这种情况下,destructor
未被调用,如here和here所述。
因此
在 Case1 中,constructor
未被调用,但调用destructor
来销毁未创建的内容,这对我来说似乎很奇怪。在 Case2 中调用构造函数,抛出异常并且不调用destructor
,这对我来说似乎是合理的。
我相信这两个例子值得进一步澄清。
修改
在 Case2 中,编译器可能使用标志来避免破坏因抛出而未构造的对象。为什么 Case1 中可以使用相同的标记来识别由于goto
而未构造的对象?
答案 0 :(得分:4)
在C ++中,为了这个原因,禁止跨变量初始化(构造函数调用):goto跳过构造函数调用。在goto和对DoSomething的调用之后,变量a
将超出范围,因此析构函数将在a
上调用,由于goto跳过构造函数调用,因此尚未构造析构函数。
VS2008在这里很宽松,当自动生成的析构函数(可能在POD上)没有效果时,允许违反标准。
答案 1 :(得分:3)
实际上案例1是无效的C ++。编译器必须为此发出诊断,并且根本不允许编译它。如果编译器无论如何编译它,它完全取决于编译器如何处理它。在这种情况下不破坏对象是明智的,但是这需要额外的机器,这意味着正确的程序需要不必要的开销(程序需要检查内部标志是否已经构造了对象)。我不认为在编译时总是可以解决是否需要进行检查,因为这可能等同于暂停问题。