【问题标题】:Return Value Optimization - C++ - Destructor calls返回值优化 - C++ - 析构函数调用
【发布时间】:2012-02-12 10:33:33
【问题描述】:

以下代码调用了4次析构函数:

#include<iostream>
using namespace std;

class A{
   public:
   A(){cout<<"A"<<endl;}
   ~A(){cout<<"~A"<<endl;}
   A f(){cout<<"F"<<endl; A b; return b;}
};

int main(){
   A a,b;
   b=a.f();
}

输出:

A
A
F
A
~A
~A
~A
~A

谁能解释一下? 我在想应该只有三个析构函数调用。

【问题讨论】:

标签: c++ nrvo rvo


【解决方案1】:

main() 中有两个对象,因此析构函数会因为它们而被调用两次f() 中有一个对象,因此析构函数将被调用 一次 次。总共 3 次(这是您所期望的,但请继续阅读...)

现在第四次为从f返回时创建的临时对象调用析构函数。这只有在根本没有 RVO 时才会发生。 RVO 是编译器的选择,这意味着它可能会优化它,也可能不会。该语言不提供任何 RVO 保证。

不管怎样,只要提高你的优化水平;我相信您最多只能看到 3 个析构函数调用。

【讨论】:

    【解决方案2】:

    main 中有 2 个对象:A a,b;,函数体中有一个对象 f()A b;,然后有一个临时对象正在被复制,并将其副本存储到 b

    在函数体中返回b时,首先创建副本,然后破坏本地b,然后将副本分配给main中声明的变量b,然后销毁该副本。

    将以下行添加到类A 定义中并查看自己:

    A(const A&) { cout << "copying" << endl; }
    

    使用Named Return Value Optimization编译器会尝试消除多余的复制构造函数和析构函数调用,这意味着函数f() 中的本地b 将被分配给main 中的变量b,而没有正在创建的副本。因此,在您的情况下,使用 RVO / NRVO 只会创建 3 个对象。

    尽管在您的情况下,有一种方法可以避免在没有 RVO 的情况下破坏此副本:

    A a;
    A b = a.f();
    

    在这种情况下,函数f() 的返回值的副本被创建并存储为变量b。这也意味着没有调用赋值运算符,只在 main 中创建了 2 个对象:af() 返回的 b 的副本。

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      你的编译器没有优化它。您是否在启用优化的情况下编译它?

      这是相同代码的输出,用 gcc 编译:

      A
      A
      F
      A
      ~A
      ~A
      ~A
      

      【讨论】:

        【解决方案4】:

        A 的实例有一个隐藏的创建和销毁:当您从函数 f() 返回时,会创建对象 b 的临时副本。在main()中分配给b,然后销毁。

        【讨论】:

        • 他在问为什么 RVO 没有发生。
        • @Luchian 你是对的,但因为他没有提到使用(或没有)优化级别,我的印象是他只是在按值返回时忽略了那个临时。
        【解决方案5】:

        您不能依赖 RVO 发生。这就是为什么你永远不应该将函数逻辑放在析构函数或复制构造函数中(是的,这些也可以省略)。

        返回值优化只是标准允许但不强制执行的事情。

        没有优化或 O2,我也得到 4 个析构函数调用。

        经过全面优化 - Ox - 我只得到 3 个。

        【讨论】:

        • /O2 还包含 NRVO,这意味着只调用了 3 个析构函数。检查msdn.microsoft.com/en-us/library/8f8h5cxt.aspx
        • @LihO 我测试了代码。所以我想如果你说的是准确的话,MSVS 中有一个错误。
        • 我也测试过它(MSVS2010)。禁用优化 (/Od) 会调用 4 个析构函数。使用/O1/O2,会调用3 个析构函数。
        【解决方案6】:

        函数返回时,f 中的局部变量被复制到临时变量中。这就是为什么有四个析构函数调用。 (复制操作调用复制构造函数A(A&amp;)而不是你的默认构造函数A(),因此三个As。)

        【讨论】:

        • 这不是问题。他在问为什么返回值优化不适用。
        • 我不确定他/她是否知道 RVO。问题只是“为什么有四个析构函数调用而不是三个?”我回答了。
        • 我以为我做到了...不过,通过查看问题,我无法决定应该在哪个级别回答。所以,我把这个答案作为贡献。
        • 当然,这是一个很好的答案,但它并没有完全解决问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多