【问题标题】:C++: Life span of temporary arguments?C++:临时参数的寿命?
【发布时间】:2011-01-31 04:44:12
【问题描述】:

当创建 MyClass 的新实例作为函数的参数时,如下所示:

class MyClass
{
  MyClass(int a);
};    

myFunction(MyClass(42));

标准是否对析构函数的时间做出任何保证?

具体来说,我可以假设它会在调用myFunction() 之后的下一条语句之前被调用吗?

【问题讨论】:

标签: c++ destructor


【解决方案1】:

临时对象在它们所属的完整表达式结束时被销毁。

完整表达式是不是某个其他表达式的子表达式的表达式。通常这意味着它在;(或) for ifwhileswitch 等)处结束,表示语句的结尾。在您的示例中,这是函数调用的结束。

请注意,您可以通过将临时对象绑定到 const 引用来延长它们的生命周期。这样做会将它们的生命周期延长到引用的生命周期:

MyClass getMyClass();

{
  const MyClass& r = getMyClass(); // full expression ends here
  ...
} // object returned by getMyClass() is destroyed here

如果您不打算更改返回的对象,那么这是一个保存复制构造函数调用的好技巧(与 MyClass obj = getMyClass(); 相比),以防未应用返回值优化。不幸的是,它不是很为人所知。 (不过,我想 C++11 的移动语义会降低它的用处。)

【讨论】:

  • +1 不错的答案。我认为您的意思是 ) 而不是 } 用于 if、while、switch 等? if(f(string())) { ... } 肯定不会让字符串存活到} :)
  • 您自己说得对:“完整的表达式是不是某个其他表达式的子表达式的表达式。”在 while、if、switch 案例中,我们在 () 之间有一个完整的表达式,因为它们出现的语句无论如何都不是表达式。在您的解释中,; 的结尾根本不会是一个完整的表达式结尾,因为如果您在一段时间/if 或 switch 之内,它周围会有一个更大的“表达式”。我认为很容易看出这是没有意义的:)
  • @Johannes:我改了。 经过这么多年的 C++,你仍然每周都能学到一些东西。
  • 我没有意识到您可以使用 const 引用来延长临时对象的寿命。很高兴知道!
  • 在 C++11 中,右值引用还延长了临时对象的生命周期。但是,出于性能原因,我不会担心使用其中任何一个。我希望任何优化编译器都在这里应用复制/移动省略,使两个版本编译为相同的代码(按值捕获或绑定到引用)。
【解决方案2】:

每个人都正确引用了 12.2/3 或类似内容,这回答了您的问题:

临时对象被销毁为 评估的最后一步 完全表达(词法上) 包含它们所在的位置 已创建。

我觉得在我打印标准时,下一页很有趣,12.2/4 说:

有两种情况 临时工被摧毁 与结束点不同的点 完整的表达。

它们都不适用于您的示例,它们都与初始化器中临时变量的使用有关。但它确实表明,在处理像 C++ 标准这样棘手的野兽时,你必须保持头脑清醒。

【讨论】:

  • 如果第二个引用是有意对齐的,它从一页开始,到下一页结束,那就太棒了。 :-)
  • 哇,感谢您提到这个“例外”。初始化程序中的临时变量正是我被咬的情况。
【解决方案3】:

该标准确实提供了保证 - 从第 12.2/5 节开始:

临时绑定到引用 函数调用中的参数 (5.2.2) 一直持续到完成 包含调用的完整表达式

但是,在您的代码中,不清楚参数是通过引用传递还是通过值传递,尽管在某些时候会使用一个确实接受引用的复制构造函数。

【讨论】:

  • 关于调用函数或被调用函数是否调用按值参数的析构函数的任何部分?检查VS2013的反汇编(clang或gcc未知),调用函数在返回之前调用析构函数。 stdcall 可以理解,因为被调用者会在返回之前弹出参数,但 cdecl 让我感到惊讶(虽然可以避免代码)。
【解决方案4】:

在第 12.2 节“临时对象”第 3 节中,ANSI/ISO C 标准规定:“......临时对象被销毁作为评估完整表达式的最后一步,该完整表达式(在词法上)包含它们的创建点。”

这与Sequence Points的概念密切相关。当到达一个序列点时,表达式的所有副作用都保证发生。

【讨论】:

    猜你喜欢
    • 2010-12-22
    • 2011-08-17
    相关资源
    最近更新 更多