5

以下代码调用析构函数 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

有人可以解释一下吗?我在想应该只有三个析构函数调用。

4

6 回答 6

8

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

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

无论如何,只要提高你的优化水平;我相信您最多只能看到 3 个析构函数调用。

于 2012-02-12T10:39:05.230 回答
3

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

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

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

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

使用Named Return Value Optimization编译器试图消除多余的 Copy 构造函数和 Destructor 调用,这意味着b函数中的 localf()将被分配给bmain 中的变量,而不会创建副本。因此,在您的情况下,使用 RVO / NRVO 仅创建 3 个对象。

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

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

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

希望这可以帮助。

于 2012-02-12T10:54:12.647 回答
1

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

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

A
A
F
A
~A
~A
~A
于 2012-02-12T10:38:53.183 回答
1

有一个隐藏的实例的创建和销毁A:当您从 function 返回时,会创建f()object 的临时副本。b它被分配到binmain()然后被销毁。

于 2012-02-12T10:41:23.560 回答
0

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

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

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

完全优化 - Ox - 我只得到 3。

于 2012-02-12T10:41:03.010 回答
0

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

于 2012-02-12T10:45:37.333 回答