右值引用调用-汇编码分析

右值引用调用-汇编码分析,第1张

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
对于C++语言来说,右值引用是在C++11时引入的一个重要的功能,并在stl库中提供了右值引用的函数,便于做相关值的转移。

下面通过汇编码分析,来进一步理解右值引用从汇编角度来看,它的特点是什么。

1. 测试代码

使用两个同名重载函数MoveStr,分别提供带右值引用的处理,和不带右值引用的处理;
基于此来看看右值引用的汇编码生成特点。

void MoveStr(std::string&& str) {
    std::string test((std::string&&)str);
}
void MoveStr(const std::string& str) {
    std::string test(str);
}
int main()
{
    std::string org(30, '1');
    MoveStr(org);
    MoveStr((std::string&&)org);
    return 0;
}
2. 汇编代码分析

从下面汇编码中分析可知:

  • 右值引用与引用从汇编参数传递角度,没有任何差别;
  • 对于右值引用或者引用调用,区别的点,只是由编译器匹配指定到到不同的函数;

下面是具体的函数分析。

主函数分析

主函数在MoveStr调用时:

MoveStr(org);
MoveStr((std::string&&)org);

MoveStr(org)是把org地址放入rcx作为输入,调用引用匹配到的MoveStr函数;
MoveStr((std::string&&)org)右值引用调用时,也是把org地址放入rcx作为输入,调用右值引用匹配到的另一个MoveStr函数;

lea rcx, [org]
call MoveStr(07FF6F3051460h)
lea rcx, [org]
call MoveStr(07FF6F3051430h)

汇编代码:

   311: int main()
   312: {
00007FF6F3051490  sub         rsp,58h  
00007FF6F3051494  mov         qword ptr [rsp+28h],0FFFFFFFFFFFFFFFEh  
   313:     std::string org(30, '1');
00007FF6F305149D  mov         r8b,31h  
00007FF6F30514A0  mov         edx,1Eh  
00007FF6F30514A5  lea         rcx,[org]  
00007FF6F30514AA  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF6F30516B0h)  
00007FF6F30514AF  nop  
   314:     MoveStr(org);
00007FF6F30514B0  lea         rcx,[org]  
00007FF6F30514B5  call        MoveStr (07FF6F3051460h)  
   315:     MoveStr((std::string&&)org);
00007FF6F30514BA  lea         rcx,[org]  
00007FF6F30514BF  call        MoveStr (07FF6F3051430h)  
   316:     return 0;
00007FF6F30514C4  mov         dword ptr [rsp+20h],0  
00007FF6F30514CC  lea         rcx,[org]  
00007FF6F30514D1  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF6F3051600h)  
00007FF6F30514D6  mov         eax,dword ptr [rsp+20h]  
   317: }
00007FF6F30514DA  add         rsp,58h  
00007FF6F30514DE  ret 
MoveStr函数分析

两个MoveStr实现的汇编码也没有多少差异;

std::string test(str);

std::string test((std::string&&)str);

都是先把输入rcx指针先存起来;
然后把输入[str]地址放入rdx,把[test]指针放入rcx作为输入,调用Copy构造函数;
不同点是:调用Copy构造函数的地址不同,为07FF6F3051630h 与 07FF6F3051710h,一个是右值引用构造,一个是普通引用构造。

mov rdx,qword ptr [str]
lea rcx, [test]
call std::basic_string<…?>::basic_string<…> (07FF6F3051630h)

mov rdx,qword ptr [str]
lea rcx, [test]
call std::basic_string<…?>::basic_string<…> (07FF6F3051710h)

汇编代码

   305: void MoveStr(std::string&& str) {
00007FF6F3051430  mov         qword ptr [rsp+8],rcx  
00007FF6F3051435  sub         rsp,48h  
   306:     std::string test((std::string&&)str);
00007FF6F3051439  mov         rdx,qword ptr [str]  
00007FF6F305143E  lea         rcx,[test]  
00007FF6F3051443  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF6F3051630h)  
   307: }
00007FF6F3051448  lea         rcx,[test]  
00007FF6F305144D  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF6F3051600h)  
   307: }
00007FF6F3051452  add         rsp,48h  
00007FF6F3051456  ret


   308: void MoveStr(const std::string& str) {
00007FF6F3051460  mov         qword ptr [rsp+8],rcx  
00007FF6F3051465  sub         rsp,48h  
   309:     std::string test(str);
00007FF6F3051469  mov         rdx,qword ptr [str]  
00007FF6F305146E  lea         rcx,[test]  
00007FF6F3051473  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF6F3051710h)  
   310: }
00007FF6F3051478  lea         rcx,[test]  
00007FF6F305147D  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF6F3051600h)  
00007FF6F3051482  add         rsp,48h  
00007FF6F3051486  ret
3. 附加分析

特点1:右值引用仅在传递参数进入时起匹配作用;进入函数内和引用一样,不能够继续透传
测试中看到str虽然按右值引用传入,但使用时,如果要继续能按右值引用继续传递到调用函数,则需要再次转右值引用;
尝试将MoveStr(std::string&& str)函数内容也写成std::string test(str),发现不再会走std::string的右值引用构造函数,而走的普通构造函数。
所以在右值引用传入MoveStr函数中:想调用std::string的右值引用构造函数,需要再次转右值引用方可。
test = (std::string&&)str;

特点2:右值引用也可以匹配到普通引用传递函数
尝试将右值引用函数MoveStr(std::string&& str)去掉时,编译器会匹配到普通引用函数MoveStr(const std::string& str);
也即当右值引用函数不存在时,使用右值引用参数时,编译器会匹配到普通引用传递函数并使用。

特点3:感觉右值引用和const语法有一定相似性
首先:感觉右值引用和const检查一样,是一个编译器处理的语法。编译完成的代码中,是看不到右值引用与引用差异,就像非const与const差异的。
区别上,const有传递性,编译器会往下层函数中继续检查;但右值引用传递性也没有,就在转成参数的时刻参与函数选择,走入函数中后就和引用没区别了,再往下传递匹配函数时需要再转。

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1295611.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存