以下代码调用析构函数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
有人可以解释一下吗? 我以为应该只有三个析构函数调用。
答案 0 :(得分:8)
main()
中有两个对象,因此析构函数将被称为两个次。 f()
中的一个对象,因此析构函数将被称为一个时间。总共3次(您期望,但继续阅读......)
现在第四次为从f
返回时创建的临时对象调用析构函数。只有在根本没有RVO时才会发生这种情况。 RVO是编译器的选择,这意味着它可以优化它,或者它可能不会。该语言不保证RVO。
无论如何,只需提高优化级别;我相信你最多只会看到3个析构函数调用。
答案 1 :(得分:3)
main中有2个对象:A a,b;
,函数体f()
中的一个对象:A b;
然后有临时对象被复制并且其副本存储到{ {1}}。
在函数体中返回b
时,首先创建复制,然后破坏本地b
,然后将复制分配到main中声明的变量b
,然后这个副本被破坏了。
将以下行添加到类b
定义并看到自己:
A
使用Named Return Value Optimization,编译器尝试消除冗余的Copy构造函数和析构函数调用,这意味着函数A(const A&) { cout << "copying" << endl; }
中的本地b
将被分配到变量main中的f()
没有创建副本。因此,使用RVO / NRVO时,只会创建3个对象。
虽然有一种方法可以避免在没有RVO的情况下破坏此副本:
b
在这种情况下,创建函数A a;
A b = a.f();
的返回值的副本并将其存储为变量f()
。这也意味着不会调用任何分配运算符,并且只在主b
中创建了2个对象,并a
返回了b
的副本。
希望这有帮助。
答案 2 :(得分:1)
您的编译器没有优化它。你是否在启用优化的情况下编译了它?
以下是使用gcc:
编译的相同代码的输出A
A
F
A
~A
~A
~A
答案 3 :(得分:1)
A
的实例有一个隐藏的创建和销毁:当您从函数f()
返回时,会创建一个对象b
的临时副本。它已分配到b
中的main()
,然后销毁。
答案 4 :(得分:0)
您不能依赖RVO。这就是为什么你永远不应该把功能逻辑放在析构函数或复制构造函数中(是的,那些也可以被省略)。
返回值优化只是标准允许的内容,但不强制执行。
没有优化或O2,我也得到4个析构函数。
完全优化 - 牛 - 我只得到3。
答案 5 :(得分:0)
当函数返回时,f
中的局部变量被复制到临时变量中。这就是为什么有四个析构函数调用。 (复制操作调用复制构造函数A(A&)
而不是默认构造函数A()
,因此调用三个A
。)