是否保证在块结束之前不会调用C ++析构函数?

时间:2010-01-18 16:41:37

标签: c++ raii

在下面的C ++代码中,我保证在<更多代码执行之后将~obj()析构函数调用吗?或者如果编译器检测到它没有被使用,是否允许编译器先前破坏它?

{
  SomeObject obj;
  ... // More code
}

我想使用这种技术来节省我必须记住在块的末尾重置一个标志,但是我需要为整个块保持设置标志。

8 个答案:

答案 0 :(得分:45)

你没关系 - 这是C ++编程中一种非常常用的模式。从C ++标准第12.4 / 10节开始,指的是何时调用析构函数:

  

用于构造对象   自动存储时间   当对象所在的块时   创建出口

答案 1 :(得分:23)

其实...

C ++有一种称为“似乎”的原则。在所有这些答案中引用的所有guarentees仅引用 observable 行为。允许编译器对任何函数调用进行排除,重新排序,添加等等,只要可观察行为就像它最初编写的那样执行。这也适用于析构函数。

所以,从技术上讲,你的观察是正确的:编译器 允许更早地破坏对象,如果它检测到它没有被使用,没有可观察的一面析构函数或它调用的任何函数的效果。但是,如果您无法在调试器之外判断出这种情况,那么您将被保证,因为如果您能够分辨,编译器将无法再进行此操作。

编译器更有可能使用这种功能做一些有用的事情,比如完全抛弃一个简单的析构函数,而不是实际重新排序析构函数调用。

编辑:有人想要一个参考文章...... 1.9 / 5,以及C ++ 0x草案标准的脚注4(这不是一个新规则,我只是没有方便的C ++ 03标准。它也存在于C标准,AFAIK)

1.9 / 5:

执行格式良好的程序的符合实现应产生相同的可观察行为 作为抽象机的相应实例的可能执行序列之一 相同的程序和相同的输入。但是,如果任何此类执行序列包含未定义的操作, 本国际标准对执行该程序的实施没有要求 输入(甚至不考虑第一个未定义操作之前的操作)。

脚注4:

此规定有时被称为“as-if”规则,因为实施可以自由地忽略对此的任何要求 国际标准,只要结果就好像已经遵守了要求,只要可以从中确定 程序的可观察行为。例如,如果可以,实际实现不需要评估表达式的一部分 推断其价值未被使用,并且不会产生影响程序可观察行为的副作用。

我的阅读(以及我认为的一般理解)是,这使得编译器可以随心所欲地做任何事情(即,启用优化),只要可观察行为是原始书面源的行为 - 包括在析构函数周围移动,根本不破坏对象,发明析构函数等。

答案 2 :(得分:18)

在对象超出范围之前,不会调用析构函数。

C++ faq lite在dtors上有一个很好的部分

答案 3 :(得分:9)

C ++中的销毁是确定性的 - 意味着编译器无法自由移动该代码。 (当然优化可能会内联析构函数,确定析构函数代码不与// More code交互并执行一些指令重新排序,但这是另一个问题)

如果你不能依赖于被调用的析构函数,你就不能使用RAII来获取锁(或者就任何其他RAII结构而言):

{
    LockClass lock(lockData);
    // More code
} // Lock automatically released.

此外,您可以依赖于以与构造对象的方式相反的顺序运行的析构函数。

答案 4 :(得分:5)

是的,保证。

具有自动存储持续时间的对象的生命周期在其潜在范围的末尾结束,而不是之前。对于这样的对象,潜在范围从声明点开始,并在声明它的块的末尾结束。这是析构函数被调用的时刻。

请注意,非常迂腐地说,即使对于自动对象,当它“超出范围”时也说它被销毁是不正确的(而不是“超出其潜在范围” “)。对象可以超出范围并多次返回范围(如果在块中声明了更多具有相同名称的本地对象),并且以这种方式超出范围不会导致对象的破坏。它的范围的“最终结束”会杀死自动对象,如上所述,自动对象被定义为潜在范围的结束

事实上,语言标准甚至不依赖于 scope 的概念来描述自动对象的生命周期(无需处理所有这些术语错综复杂)。它只是说对象在定义它的块的出口处被销毁:)

答案 5 :(得分:3)

是的,C ++标准对于何时销毁对象有非常具体的要求(在§12.4/ 10中),并且在这种情况下,在块中的所有其他代码完成执行之前不得销毁它。

答案 6 :(得分:2)

这里的所有答案都解决了命名对象的问题,但为了完整起见,您可能也应该知道临时/匿名对象的规则。 (例如f(SomeObjectConstructor()f(someFunctionThatReturnsAnObject())

  

临时对象作为评估全表达式(1.9)的最后一步被销毁,该表达式(词法上)包含创建它们的点。即使该评估以抛出异常结束,也是如此。 (ISO C ++ 98标准中的12.2 / 3)

这基本上意味着临时生成的对象会持续到下一个语句。两个例外是作为对象初始化列表的一部分生成的临时对象(在这种情况下,临时仅在对象完全构造之后被销毁)并且如果引用临时(例如const Foo& ref = someFunctionThatReturnsAnobject())(这种情况下)对象的生命周期是引用的生命周期。

答案 7 :(得分:1)

一个典型的例子,就像你的问题是boost :: scoped_ptr(或类似的std :: auto_ptr):

{
    boost::scoped_ptr< MyClass > pMyClass( new MyClass );

    // code using pMyClass here

} // destruction of MyClass and memory freed