我刚编译了这个简单的代码段:
#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仍显示两个析构函数。谁能解释我为什么?
答案 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
来实现重新分析。这是展开库中唯一可以直接由生成的代码调用的例程:它将在“登陆垫”模型中的着陆点的末尾调用。