为什么我真的只需要一个,却创建了两个对象
Why are there two objects created when I only really need one?
我刚刚编译了这个简单的片段:
#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仍然显示两个被调用的析构函数。有人能解释一下为什么吗?
第一个析构函数调用后面跟着几个指令,然后是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
这是正常的代码流。
从400932
的后续mov
开始,是内部用于展开具有异常传播的堆栈的代码,通常称为着陆台。
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>
以下是GCC要说的:
生成异常处理着陆台
此过程生成用于处理异常处理库例程和函数内异常处理程序之间通信的粘合。异常处理库调用的函数中的入口点称为着陆台。此通行证的代码位于
except.c
中。
正如您所看到的,控制流的路径是完全不同的,因此无论哪种方式,析构函数都只能调用一次。
_Unwind_Resume
是AMD64和安腾C++ABI的一部分,作为展开调用堆栈直到到达能够捕获异常类型的函数的一种方法。你需要做一些挖掘,才能从谷歌上找到更多关于它的信息。这里有一个很好的资源来讨论它。
_展开资源
void _Unwind_Resume (struct _Unwind_Exception *exception_object);
恢复现有异常的传播,例如在部分展开的堆栈中执行清理代码之后。对该例程的调用插入到执行清理但未恢复正常执行的着陆台的末尾。这会导致退市进一步进行。
_Unwind_Resume
不应用于实现重新思考。对于展开运行时,重新引发的catch代码是一个处理程序,并且在进入前一个展开会话已终止。通过使用相同的异常对象再次调用_Unwind_RaiseException
来实现重新引发。这是展开库中唯一一个预期由生成的代码直接调用的例程:它将在"着陆台"模型中的着陆台末端调用。
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何创建对象函数指针C++映射?
- C++创建对象数组
- 在 C++ 的 Switch Case 中创建对象后对对象调用方法
- 如何获取在 main() 函数中构造的类的创建对象?
- 基于文件中的条目创建对象
- 错误:创建对象后无法分配区域
- C++ 通过输入创建对象
- 堆还是堆栈用于创建对象?
- 使用 C++ 创建对象数组
- 使用unique_ptr创建对象
- C++递归地在类构造函数中创建对象
- 通过向构造函数其他对象引用页面来创建对象
- ReactiveX (rx) - 在对象上应用可观察对象,而不是在可观察对象中创建对象
- 如何在OSX上正确创建C++对象文件(.o)
- 编译器是否会创建vtable,而不考虑在c++中创建对象
- 创建对象并防止被破坏
- 在C++中,友元类可以从友元类创建对象吗
- 只在堆中创建C++对象
- 创建用户定义的复制构造函数时无法创建对象