函数返回另一个函数的返回
时间:2020-03-06 15:03:58 来源:igfitidea点击:
如果我想调用Bar()而不是Foo(),Bar()是否向我返回Foo()返回的内容的副本(添加开销),或者它返回的对象与Foo()相同`放在临时堆栈上吗?
vector<int> Foo(){ vector<int> result; result.push_back(1); return result; } vector<int> Bar(){ return Foo(); }
解决方案
通常,它返回返回的vector <int>
的副本。但是,这在很大程度上取决于编译器所做的优化。请参阅以下讨论。
调试版本
vector<int> Foo(){ 004118D0 push ebp 004118D1 mov ebp,esp 004118D3 push 0FFFFFFFFh 004118D5 push offset __ehhandler$?Foo@@YA?AV?$vector@HV?$allocator@H@std<std@@XZ (419207h) 004118DA mov eax,dword ptr fs:[00000000h] 004118E0 push eax 004118E1 sub esp,0F4h 004118E7 push ebx 004118E8 push esi 004118E9 push edi 004118EA lea edi,[ebp-100h] 004118F0 mov ecx,3Dh 004118F5 mov eax,0CCCCCCCCh 004118FA rep stos dword ptr es:[edi] 004118FC mov eax,dword ptr [___security_cookie (41E098h)] 00411901 xor eax,ebp 00411903 push eax 00411904 lea eax,[ebp-0Ch] 00411907 mov dword ptr fs:[00000000h],eax 0041190D mov dword ptr [ebp-0F0h],0 vector<int> result; 00411917 lea ecx,[ebp-24h] 0041191A call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (411050h) 0041191F mov dword ptr [ebp-4],1 result.push_back(1); 00411926 mov dword ptr [ebp-0FCh],1 00411930 lea eax,[ebp-0FCh] 00411936 push eax 00411937 lea ecx,[ebp-24h] 0041193A call std::vector<int,std::allocator<int> >::push_back (41144Ch) return result; 0041193F lea eax,[ebp-24h] 00411942 push eax 00411943 mov ecx,dword ptr [ebp+8] 00411946 call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (41104Bh) 0041194B mov ecx,dword ptr [ebp-0F0h] 00411951 or ecx,1 00411954 mov dword ptr [ebp-0F0h],ecx 0041195A mov byte ptr [ebp-4],0 0041195E lea ecx,[ebp-24h] 00411961 call std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> > (411415h) 00411966 mov eax,dword ptr [ebp+8] }
在这里我们可以看到,对于vector <int> result;
,在堆栈上的[ebp-24h]中创建了一个新对象。
00411917 lea ecx,[ebp-24h] 0041191A call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (411050h)
当我们返回结果时,将在调用者分配的存储空间中创建一个新副本,该副本位于[ebp + 8]。
00411943 mov ecx,dword ptr [ebp+8] 00411946 call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (41104Bh)
然后在[ebp-24h]处为本地参数vector <int> result
调用析构函数。
0041195E lea ecx,[ebp-24h] 00411961 call std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> > (411415h)
发布版本
vector<int> Foo(){ 00401110 push 0FFFFFFFFh 00401112 push offset __ehhandler$?Foo@@YA?AV?$vector@HV?$allocator@H@std<std@@XZ (401F89h) 00401117 mov eax,dword ptr fs:[00000000h] 0040111D push eax 0040111E sub esp,14h 00401121 push esi 00401122 mov eax,dword ptr [___security_cookie (403018h)] 00401127 xor eax,esp 00401129 push eax 0040112A lea eax,[esp+1Ch] 0040112E mov dword ptr fs:[00000000h],eax 00401134 mov esi,dword ptr [esp+2Ch] 00401138 xor eax,eax 0040113A mov dword ptr [esp+8],eax vector<int> result; 0040113E mov dword ptr [esi+4],eax 00401141 mov dword ptr [esi+8],eax 00401144 mov dword ptr [esi+0Ch],eax result.push_back(1); return result; 00401147 push eax 00401148 mov dword ptr [esp+28h],eax 0040114C mov ecx,1 00401151 push esi 00401152 lea eax,[esp+14h] 00401156 mov dword ptr [esp+10h],ecx 0040115A mov dword ptr [esp+14h],ecx 0040115E push eax 0040115F lea ecx,[esp+1Ch] 00401163 push ecx 00401164 mov eax,esi 00401166 call std::vector<int,std::allocator<int> >::insert (401200h) 0040116B mov eax,esi } 0040116D mov ecx,dword ptr [esp+1Ch] 00401171 mov dword ptr fs:[0],ecx 00401178 pop ecx 00401179 pop esi 0040117A add esp,20h 0040117D ret
" vector <int> result"行不调用向量分配器,因为它是在" Bar"中的调用位置完成的。该优化不会复制Foo的结果。
两者都有可能发生。但是,大多数编译器在优化后不会立即进行复制。
代码表明应该有一个副本。但是,允许编译器删除任何不会更改语义和程序的副本。
注意:这就是为什么我们不应该拥有一个复制构造函数,该复制构造函数除了正确复制外什么也不做,因为我们永远无法确定复制是否会真正完成。
对于NRVO名称返回值优化来说,这是一个琐碎的情况(在这种情况下,由于没有名称,因此使用不当)。斯坦·李普曼(Stan Lippman)撰写了一篇博客文章,其中很好地解释了所涉及的机制。