3

考虑以下代码段:

#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()此处调用了两次(但仅构造了 1 个临时 obj)。这看起来很奇怪,因为 1) 返回的 temp 变量GetTemp()的生命周期应该扩展到完整的表达式,以及 2) 由于我们temp直接返回 in TakeTemp,因此返回值优化将重用相同的对象。

谁能解释为什么这里有多个 dstor 调用?

(请注意,如果我们放置更多的 TakeTemp() 层,dstor 调用的数量会成比例地增长。)

4

2 回答 2

5

您的函数TakeTemp按值获取其参数,并按值返回参数。

您正在那里制作副本,因此现在有两个Temp要删除的对象。

您看到被破坏的两个对象是此处调用的两个函数的返回值:

TakeTemp(GetTemp());
         ^ returns a Temp
^ returns a Temp
于 2019-01-31T08:34:22.097 回答
1

使用 C++17 术语,这两个对象是:

  1. 功能参数Temp temp;
  2. 的返回值TakeTemp

函数调用GetTemp()是prvalue。由于它是函数调用的参数,因此它的结果对象是匹配的参数Temp temp临时实现Temp temp转换应用于.

请注意,函数内部没有临时创建GetTemp()。该声明return Temp();并不意味着创建一个对象;它给出的参数将用于稍后最终创建一个对象。在实现纯右值之前不会创建任何对象。

然后,执行return temp;创建第二个对象。这不同于return Temp();因为temp是左值,而不是纯右值。函数调用的返回值对象TakeTemp是使用temp初始化器创建的。这不是复制省略上下文。如果向其中添加复制构造函数,Temp您将看到该对象的复制构造消息。

回顾一下,事件的顺序是:

  • GetTemp身体被输入
  • GetTemp执行return语句,初始化参数Temp temp
  • GetTemp主体退出(如果有的话,销毁任何局部变量)
  • TakeTemp身体被输入
  • TakeTemp执行return语句,初始化返回值对象TakeTemp
  • TakeTemp主体退出(执行返回到main
  • { 参数Temp temp被销毁
  • { 的返回值对象TakeTemp被销毁

的生命Temp temp周期是函数参数的生命周期;它在函数返回后被销毁。

的返回值的生命周期TakeTemp是一个临时对象生命周期,所以它会一直持续到完整表达式的末尾。

请注意,函数参数生命周期有一个怪癖:它是由实现定义的,它是在调用之后立即销毁还是在完整表达式的末尾销毁。因此,我上面列表中的最后两个步骤可以按任意顺序发生。在我安装的 g++ 8.2.1 中,函数参数实际上是两个析构函数中的后者。

于 2019-01-31T08:51:54.510 回答