当我只需要一个对象时,为什么会创建两个对象?

时间:2012-08-29 01:12:47

标签: c++ c++11 rvalue-reference

我刚编译了这个简单的代码段:

#include <iostream>
#include <string>

std::string foo()
{
    return std::string("bar");
}

int main()
{
    std::string test = foo();
    std::cout << test << std::endl;
    return 0;
}

使用-O2优化,只发现正在创建两个std :: string对象。当我转储二进制文件时,objdump显示~basic_string被调用两次。

0000000000400900 <main>:
  400900:   53                      push   %rbx
  400901:   48 83 ec 10             sub    $0x10,%rsp
  400905:   48 89 e7                mov    %rsp,%rdi
  400908:   e8 73 01 00 00          callq  400a80 <foo()>
  40090d:   48 89 e6                mov    %rsp,%rsi
  400910:   bf 80 10 60 00          mov    $0x601080,%edi
  400915:   e8 a6 ff ff ff          callq  4008c0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@plt>
  40091a:   48 89 c7                mov    %rax,%rdi
  40091d:   e8 ae ff ff ff          callq  4008d0 <std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@plt>
  400922:   48 89 e7                mov    %rsp,%rdi
  400925:   e8 76 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  40092a:   48 83 c4 10             add    $0x10,%rsp
  40092e:   31 c0                   xor    %eax,%eax
  400930:   5b                      pop    %rbx
  400931:   c3                      retq   
  400932:   48 89 c3                mov    %rax,%rbx
  400935:   48 89 e7                mov    %rsp,%rdi
  400938:   e8 63 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  40093d:   48 89 df                mov    %rbx,%rdi
  400940:   e8 ab ff ff ff          callq  4008f0 <_Unwind_Resume@plt>
  400945:   66 66 2e 0f 1f 84 00    data32 nopw %cs:0x0(%rax,%rax,1)
  40094c:   00 00 00 00 

因为我只需要一个对象,所以我考虑使用右值引用来捕获返回的值foo()。所以我将代码行更改为std::string && test = foo();奇怪的是,objdump仍显示两个析构函数。谁能解释我为什么?

1 个答案:

答案 0 :(得分:9)

第一个析构函数调用稍后会按照retq

进行一些说明
  400925:   e8 76 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  ...
  400931:   c3                      retq   

这是正常代码流。

mov开始,400932是内部用于通过异常传播展开堆栈的代码,通常称为着陆点

  400932:   48 89 c3                mov    %rax,%rbx
  400935:   48 89 e7                mov    %rsp,%rdi
  400938:   e8 63 ff ff ff          callq  4008a0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@plt>
  40093d:   48 89 df                mov    %rbx,%rdi
  400940:   e8 ab ff ff ff          callq  4008f0 <_Unwind_Resume@plt>

Here是GCC所说的:

  
      
  • 生成异常处理登陆垫

         

    此过程生成粘合,处理异常处理库例程与函数内的异常处理程序之间的通信。函数中由异常处理库调用的入口点称为着陆垫。此通行证的代码位于except.c

  •   

正如您所看到的,控制流的路径是完全不同的,因此析构函数只会被调用一次。

_Unwind_Resume是AMD64和Itanium C ++ ABI的一部分,它是一种解除调用堆栈的方法,直到它到达能够捕获异常类型的函数。您需要进行一些挖掘,以便从Google上找到有关它的更多信息。 Here是一个很好的资源,可以讨论它。

  

_ <强> Unwind_Resume

void _Unwind_Resume
(struct _Unwind_Exception *exception_object);
     

恢复现有例外的传播,例如在部分展开的堆栈中执行清理代码之后。在执行清理的着陆板的末尾插入对此例程的调用,但未恢复正常执行。它导致放松进一步。

     

_Unwind_Resume不应用于实施重新抛出。对于展开运行时,重新抛出的catch代码是一个处理程序,之前的展开会话在进入之前就被终止了。通过使用相同的异常对象再次调用_Unwind_RaiseException来实现重新分析。

     

这是展开库中唯一可以直接由生成的代码调用的例程:它将在“登陆垫”模型中的着陆点的末尾调用。