【问题标题】:Function returning the return of another function函数返回另一个函数的返回值
【发布时间】:2010-09-15 00:46:24
【问题描述】:

如果我想调用 Bar() 而不是 Foo(),Bar() 是否会返回 Foo() 返回的副本(额外开销),或者它返回 Foo() 放置在临时对象上的相同对象堆栈?

vector<int> Foo(){  
    vector<int> result;  
    result.push_back(1);  
    return result;  
}  
vector<int> Bar(){  
    return Foo();  
}

【问题讨论】:

    标签: c++ function vector return-value


    【解决方案1】:

    两者都可能发生。但是,大多数编译器不会在您优化后立即进行复制。

    您的代码表明应该有一个副本。但是,允许编译器删除任何不改变语义和程序的副本。

    注意:这就是为什么你永远不应该有一个除了正确复制之外做任何事情的复制构造函数,因为你永远无法确定复制是否会真正完成。

    【讨论】:

    • 不幸的是,这不是允许编译器从中删除副本的地方(尽管内联会删除它)。所以是的,向量从 Foo() 复制到 Bar() 然后从 Bar() 复制到调用者。为了使复制 std::vector 非常有效,已经做了很多工作。所以不用担心。
    • 我认为编译器可以使用返回值优化 (msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx) 将其安全地优化为单个副本。基本上,如果变量将被构造并立即复制,它可以直接构造到将被复制的位置。
    • 这绝对是优化的地方,即使没有内联。
    • 如果这两个函数在不同的类中会不会也被优化?其实这是我想问但没有正确提及条件的实际问题。
    【解决方案2】:

    这是 NRVO 的一个小例子——名称返回值优化(在这种情况下用词不当,因为没有名称)。 Stan Lippman 向blog entry 致敬,并很好地解释了所涉及的机制。

    【讨论】:

    • 临时不命名时不就是叫RVO吗。
    • @David:是的,有人这么说,我自己也这么称呼它。然而,技术文献中似乎没有提到“RVO”。 “NRVO”用作包含此内容的技术术语。
    【解决方案3】:

    通常它会返回返回的vector&lt;int&gt; 的副本。然而,这在很大程度上取决于编译器所做的优化。请参阅以下讨论。

    调试构建

    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&lt;int&gt; result;,在[ebp-24h] 的堆栈上创建了一个新对象

    00411917  lea         ecx,[ebp-24h] 
    0041191A  call        std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (411050h)
    

    当我们到达return result; 时,一个新副本会在调用者在[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&lt;int&gt; 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&lt;int&gt; result 行不调用向量分配器,因为它是在Bar 的调用站点完成的。优化不会复制 Foo 的结果。

    【讨论】:

    • 我确实认为这种解释有点矫枉过正......而且高度依赖于编译器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-27
    • 2014-01-03
    • 1970-01-01
    相关资源
    最近更新 更多