按值函数创建范围的临时对象

Temporary object for by-value function creation scope

本文关键字:临时对象 范围 创建 函数      更新时间:2023-10-16

我有以下代码,我无法理解:

#include <cstdio>
#include <iostream>
using namespace std;
class A 
{
    public: 
        int t = 0;
    A(){
      cout << "constructed"  << t<< endl; 
    } 
 A (A&& a) {
    cout << "in move ctor, moving"<< a.t << endl;
 }
 ~A() { 
     cout << "deleting"<< t << endl;
    }
};
A f1 (A a) 
{ 
    a.t = 1;
    std::cout << "f1: " << endl; 
    return a; 
}
int main() {
    A a = f1(A()) ;
    printf("what is happeningn");
}

输出为

constructed0
in move ctor, moving0
f1: 
in move ctor, moving1
in move ctor, moving0
deleting0
deleting1
deleting0
what is happening
deleting0

我无法理解的是 f1 创建的临时对象(a.t=1 的对象(被销毁的阶段。

从输出中,我假设它在行A a = f1(A()) ;的 and 处被销毁 虽然我认为它是在 f1 和 f1 中创建的,因此在调用 deleting0 之前退出函数时将被销毁。

我错过了什么?

所以经过一番研究,我有了答案。

下面是代码的反汇编(为了便于阅读,将移动构造函数更改为复制构造函数(:

int A::counter = 0;
A f1 (A a) 
{ 
  400a18:   55                      push   %rbp
  400a19:   48 89 e5                mov    %rsp,%rbp
  400a1c:   48 83 ec 10             sub    $0x10,%rsp
  400a20:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
  400a24:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
    cout << __LINE__ << endl;
  400a28:   be 1d 00 00 00          mov    $0x1d,%esi
  400a2d:   bf 80 13 60 00          mov    $0x601380,%edi
  400a32:   e8 c1 fd ff ff          callq  4007f8 <_ZNSolsEi@plt>
  400a37:   be 78 08 40 00          mov    $0x400878,%esi
  400a3c:   48 89 c7                mov    %rax,%rdi
  400a3f:   e8 24 fe ff ff          callq  400868 <_ZNSolsEPFRSoS_E@plt>
    a.t = 1;
  400a44:   48 8b 45 f0             mov    -0x10(%rbp),%rax
  400a48:   c7 00 01 00 00 00       movl   $0x1,(%rax)
    std::cout << "f1: " << endl; 
  400a4e:   be ce 0e 40 00          mov    $0x400ece,%esi
  400a53:   bf 80 13 60 00          mov    $0x601380,%edi
  400a58:   e8 fb fd ff ff          callq  400858 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400a5d:   be 78 08 40 00          mov    $0x400878,%esi
  400a62:   48 89 c7                mov    %rax,%rdi
  400a65:   e8 fe fd ff ff          callq  400868 <_ZNSolsEPFRSoS_E@plt>
    cout << __LINE__ << endl;
  400a6a:   be 20 00 00 00          mov    $0x20,%esi
  400a6f:   bf 80 13 60 00          mov    $0x601380,%edi
  400a74:   e8 7f fd ff ff          callq  4007f8 <_ZNSolsEi@plt>
  400a79:   be 78 08 40 00          mov    $0x400878,%esi
  400a7e:   48 89 c7                mov    %rax,%rdi
  400a81:   e8 e2 fd ff ff          callq  400868 <_ZNSolsEPFRSoS_E@plt>
    return a; 
  400a86:   48 8b 55 f0             mov    -0x10(%rbp),%rdx
  400a8a:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400a8e:   48 89 d6                mov    %rdx,%rsi
  400a91:   48 89 c7                mov    %rax,%rdi
  400a94:   e8 dd 01 00 00          callq  400c76 <_ZN1AC1ERKS_>
}
  400a99:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400a9d:   c9                      leaveq 
  400a9e:   c3                      retq   
0000000000400a9f <main>:
int main() {
  400a9f:   55                      push   %rbp
  400aa0:   48 89 e5                mov    %rsp,%rbp
  400aa3:   53                      push   %rbx
  400aa4:   48 83 ec 48             sub    $0x48,%rsp
    A a = f1(A()) ;
  400aa8:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  400aac:   48 89 c7                mov    %rax,%rdi
  400aaf:   e8 2a 01 00 00          callq  400bde <_ZN1AC1Ev>
  400ab4:   48 8d 55 e0             lea    -0x20(%rbp),%rdx
  400ab8:   48 8d 45 d0             lea    -0x30(%rbp),%rax
  400abc:   48 89 d6                mov    %rdx,%rsi
  400abf:   48 89 c7                mov    %rax,%rdi
  400ac2:   e8 af 01 00 00          callq  400c76 <_ZN1AC1ERKS_>
  400ac7:   48 8d 45 c0             lea    -0x40(%rbp),%rax
  400acb:   48 8d 55 d0             lea    -0x30(%rbp),%rdx
  400acf:   48 89 d6                mov    %rdx,%rsi
  400ad2:   48 89 c7                mov    %rax,%rdi
  400ad5:   e8 3e ff ff ff          callq  400a18 <_Z2f11A>
  400ada:   48 8d 55 c0             lea    -0x40(%rbp),%rdx
  400ade:   48 8d 45 b0             lea    -0x50(%rbp),%rax
  400ae2:   48 89 d6                mov    %rdx,%rsi
  400ae5:   48 89 c7                mov    %rax,%rdi
  400ae8:   e8 89 01 00 00          callq  400c76 <_ZN1AC1ERKS_>
  400aed:   48 8d 45 c0             lea    -0x40(%rbp),%rax
  400af1:   48 89 c7                mov    %rax,%rdi
  400af4:   e8 31 02 00 00          callq  400d2a <_ZN1AD1Ev>
  400af9:   48 8d 45 d0             lea    -0x30(%rbp),%rax
  400afd:   48 89 c7                mov    %rax,%rdi
  400b00:   e8 25 02 00 00          callq  400d2a <_ZN1AD1Ev>
  400b05:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  400b09:   48 89 c7                mov    %rax,%rdi
  400b0c:   e8 19 02 00 00          callq  400d2a <_ZN1AD1Ev>
    printf("what is happeningn");
  400b11:   bf d3 0e 40 00          mov    $0x400ed3,%edi
  400b16:   e8 ed fc ff ff          callq  400808 <puts@plt>
    cout << __LINE__ << endl;
    return a; 
}

复制构造函数在重整过程后称为">ZN1AC1ERKS"。
正如我们所看到的,为 f1 创建的临时对象是在函数调用之前创建的,主要是在 f1 的作用域中创建的,而不是像我预期的那样。

含义如下:
为按值调用的函数创建的临时对象不是在函数范围内创建的,而是在称为函数的行中创建的,因此它们将在下一行执行之前以普通的先创建最后销毁方式被销毁。