请考虑以下代码段:
#include <iostream>
using namespace std;
class Temp {
public:
Temp() { cout << "Temp()" << endl;}
~Temp() { cout << "~Temp()" << endl;}
};
Temp GetTemp() {
cout << "GetTemp" << endl;
return Temp();
}
Temp TakeTemp(Temp temp) {
cout << "TakeTemp" << endl;
return temp;
}
int main()
{
TakeTemp(GetTemp());
return 0;
}
我运行TakeTemp(GetTemp());
时,输出看起来像
GetTemp
Temp()
TakeTemp
~Temp()
~Temp()
请注意,~Temp()
在这里被调用了两次(但仅构造了一个临时obj)。这似乎很奇怪,因为1)应该将GetTemp()
返回的temp变量的寿命延长到完整表达式,以及2)因为我们直接在temp
中返回了TakeTemp
,所以返回值优化将重用同一对象。
有人可以解释为什么这里有多个dstor呼叫吗?
(请注意,如果我们在TakeTemp()周围放置更多层,则dstor调用的数量将成比例增加。)
答案 0 :(得分:5)
您的函数TakeTemp
按值获取参数,然后按值返回参数。
您要在此处制作副本,因此现在有两个Temp
对象要删除。
您看到的两个被破坏的对象是此处调用的两个函数的返回值:
TakeTemp(GetTemp());
^ returns a Temp
^ returns a Temp
答案 1 :(得分:1)
使用C ++ 17术语,这两个对象是:
Temp temp;
TakeTemp
的返回值。函数调用GetTemp()
是一个prvalue。由于它是函数调用的参数,因此其结果对象是匹配的参数Temp temp
。在构建Temp temp
时应用临时物化转换。
请注意,GetTemp()
函数内部没有创建临时文件。语句return Temp();
并不意味着要创建一个对象。它提供了用于以后最终创建对象的参数。在实现prvalue之前,不会创建任何对象。
然后,return temp;
的执行将创建第二个对象。这与return Temp();
不同,因为temp
是左值,而不是prvalue。使用TakeTemp
作为初始化程序创建了对temp
的函数调用的返回值对象。这不是复制省略上下文。如果将复制构造函数添加到Temp
,您将看到该对象的复制构造消息。
回顾一下,事件的顺序是:
GetTemp
正文GetTemp
return语句已执行,它初始化了参数Temp temp
GetTemp
主体退出(如果有的话,销毁任何局部变量)TakeTemp
正文TakeTemp
return语句被执行,它初始化TakeTemp
的返回值对象TakeTemp
正文退出(执行返回到main
)Temp temp
被破坏TakeTemp
的返回值对象已损坏 Temp temp
的生存期是函数参数的生存期;函数返回后将其销毁。
返回值TakeTemp
的生存期是一个临时对象的生存期,因此它将一直持续到完整表达式的结尾。
请注意,有一个古怪的函数参数为生存期:it is implementation-defined是在调用之后还是在完整表达式结束时销毁。因此,以上列表中的最后两个步骤可以按任意顺序发生。在我安装的g ++ 8.2.1中,function参数实际上是两个析构函数中的较晚一个。