【发布时间】:2011-01-31 04:44:12
【问题描述】:
当创建 MyClass 的新实例作为函数的参数时,如下所示:
class MyClass
{
MyClass(int a);
};
myFunction(MyClass(42));
标准是否对析构函数的时间做出任何保证?
具体来说,我可以假设它会在调用myFunction() 之后的下一条语句之前被调用吗?
【问题讨论】:
标签: c++ destructor
当创建 MyClass 的新实例作为函数的参数时,如下所示:
class MyClass
{
MyClass(int a);
};
myFunction(MyClass(42));
标准是否对析构函数的时间做出任何保证?
具体来说,我可以假设它会在调用myFunction() 之后的下一条语句之前被调用吗?
【问题讨论】:
标签: c++ destructor
临时对象在它们所属的完整表达式结束时被销毁。
完整表达式是不是某个其他表达式的子表达式的表达式。通常这意味着它在;(或) for if、while、switch 等)处结束,表示语句的结尾。在您的示例中,这是函数调用的结束。
请注意,您可以通过将临时对象绑定到 const 引用来延长它们的生命周期。这样做会将它们的生命周期延长到引用的生命周期:
MyClass getMyClass();
{
const MyClass& r = getMyClass(); // full expression ends here
...
} // object returned by getMyClass() is destroyed here
如果您不打算更改返回的对象,那么这是一个保存复制构造函数调用的好技巧(与 MyClass obj = getMyClass(); 相比),以防未应用返回值优化。不幸的是,它不是很为人所知。 (不过,我想 C++11 的移动语义会降低它的用处。)
【讨论】:
if(f(string())) { ... } 肯定不会让字符串存活到} :)
( 和 ) 之间有一个完整的表达式,因为它们出现的语句无论如何都不是表达式。在您的解释中,; 的结尾根本不会是一个完整的表达式结尾,因为如果您在一段时间/if 或 switch 之内,它周围会有一个更大的“表达式”。我认为很容易看出这是没有意义的:)
每个人都正确引用了 12.2/3 或类似内容,这回答了您的问题:
临时对象被销毁为 评估的最后一步 完全表达(词法上) 包含它们所在的位置 已创建。
我觉得在我打印标准时,下一页很有趣,12.2/4 说:
有两种情况 临时工被摧毁 与结束点不同的点 完整的表达。
它们都不适用于您的示例,它们都与初始化器中临时变量的使用有关。但它确实表明,在处理像 C++ 标准这样棘手的野兽时,你必须保持头脑清醒。
【讨论】:
该标准确实提供了保证 - 从第 12.2/5 节开始:
临时绑定到引用 函数调用中的参数 (5.2.2) 一直持续到完成 包含调用的完整表达式
但是,在您的代码中,不清楚参数是通过引用传递还是通过值传递,尽管在某些时候会使用一个确实接受引用的复制构造函数。
【讨论】:
在第 12.2 节“临时对象”第 3 节中,ANSI/ISO C 标准规定:“......临时对象被销毁作为评估完整表达式的最后一步,该完整表达式(在词法上)包含它们的创建点。”
这与Sequence Points的概念密切相关。当到达一个序列点时,表达式的所有副作用都保证发生。
【讨论】: