对引用的深入理解

对引用的深入理解

  引用名是变量的一个别名,这就暗示了两点:

  • 已经存在了某个变量;
  • 它是某个已经存在的这个变量的另一个名字

  把引用初始化为某个变量之后,就可以使用这个变量的引用来指向变量。

  基本形式:类型标识符& 引用名 = 目标变量名;

  引用的本质可以理解为所引用对象的地址

  通过下面反汇编 的代码理解一下:
  反汇编的实现可以参考使用VS和codeblocks实现反汇编

#include <iostream>

using namespace std;

int main()
{
    int a = 1;
    int &b = a;
    cout << "a: " << a<<endl;
    cout << "b: " << b<<endl;

    cout<<"a 的地址:"<<&a<<endl;
    cout<<"b 的地址:"<<&b<<endl;
    return 0;
}

  输出:

a: 1
b: 1
a 的地址:0x6dfee8
b 的地址:0x6dfee8

  下面是在VS上反汇编之后的代码:

int main()
{
00B91490  push        ebp  
00B91491  mov         ebp,esp  
00B91493  sub         esp,0D8h  
00B91499  push        ebx  
00B9149A  push        esi  
00B9149B  push        edi  
00B9149C  lea         edi,[ebp-0D8h]  
00B914A2  mov         ecx,36h  
00B914A7  mov         eax,0CCCCCCCCh  
00B914AC  rep stos    dword ptr es:[edi]  
    int a = 1;
00B914AE  mov         dword ptr [a],1  
    int &b = a;
00B914B5  lea         eax,[a]  
00B914B8  mov         dword ptr [b],eax  
    cout << "a: " << a<<endl;
00B914BB  mov         esi,esp  
00B914BD  mov         eax,dword ptr [__imp_std::endl (0B9A318h)]  
00B914C2  push        eax  
00B914C3  mov         edi,esp  
00B914C5  mov         ecx,dword ptr [a]  
00B914C8  push        ecx  
00B914C9  push        offset string "a: " (0B97854h)  
00B914CE  mov         edx,dword ptr [__imp_std::cout (0B9A31Ch)]  
00B914D4  push        edx  
00B914D5  call        std::operator<<<std::char_traits<char> > (0B9114Ah)  
00B914DA  add         esp,8  
00B914DD  mov         ecx,eax  
00B914DF  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A320h)]  
00B914E5  cmp         edi,esp  
00B914E7  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
00B914EC  mov         ecx,eax  
00B914EE  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A324h)]  
00B914F4  cmp         esi,esp  
00B914F6  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
    cout << "b: " << b<<endl;
00B914FB  mov         esi,esp  
00B914FD  mov         eax,dword ptr [__imp_std::endl (0B9A318h)]  
00B91502  push        eax  
00B91503  mov         edi,esp  
00B91505  mov         ecx,dword ptr [b]  
00B91508  mov         edx,dword ptr [ecx]  
00B9150A  push        edx  
00B9150B  push        offset string "b: " (0B97850h)  
00B91510  mov         eax,dword ptr [__imp_std::cout (0B9A31Ch)]  
00B91515  push        eax  
00B91516  call        std::operator<<<std::char_traits<char> > (0B9114Ah)  
00B9151B  add         esp,8  
00B9151E  mov         ecx,eax  
00B91520  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A320h)]  
00B91526  cmp         edi,esp  
00B91528  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
00B9152D  mov         ecx,eax  
00B9152F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A324h)]  
00B91535  cmp         esi,esp  
00B91537  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  

    cout<<"a 的地址:"<<&a<<endl;
00B9153C  mov         esi,esp  
00B9153E  mov         eax,dword ptr [__imp_std::endl (0B9A318h)]  
00B91543  push        eax  
00B91544  mov         edi,esp  
00B91546  lea         ecx,[a]  
00B91549  push        ecx  
00B9154A  push        offset string "a \xb5\xc4\xb5\xd8\xd6\xb7\xa3\xba" (0B97840h)  
00B9154F  mov         edx,dword ptr [__imp_std::cout (0B9A31Ch)]  
00B91555  push        edx  
00B91556  call        std::operator<<<std::char_traits<char> > (0B9114Ah)  
00B9155B  add         esp,8  
00B9155E  mov         ecx,eax  
00B91560  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A304h)]  
00B91566  cmp         edi,esp  
00B91568  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
00B9156D  mov         ecx,eax  
00B9156F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A324h)]  
00B91575  cmp         esi,esp  
00B91577  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
    cout<<"b 的地址:"<<&b<<endl;
00B9157C  mov         esi,esp  
00B9157E  mov         eax,dword ptr [__imp_std::endl (0B9A318h)]  
00B91583  push        eax  
00B91584  mov         edi,esp  
00B91586  mov         ecx,dword ptr [b]  
00B91589  push        ecx  
00B9158A  push        offset string "b \xb5\xc4\xb5\xd8\xd6\xb7\xa3\xba" (0B97830h)  
00B9158F  mov         edx,dword ptr [__imp_std::cout (0B9A31Ch)]  
00B91595  push        edx  
00B91596  call        std::operator<<<std::char_traits<char> > (0B9114Ah)  
00B9159B  add         esp,8  
00B9159E  mov         ecx,eax  
00B915A0  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A304h)]  
00B915A6  cmp         edi,esp  
00B915A8  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
00B915AD  mov         ecx,eax  
00B915AF  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A324h)]  
00B915B5  cmp         esi,esp  
00B915B7  call        @ILT+400(__RTC_CheckEsp) (0B91195h)  
    return 0;
00B915BC  xor         eax,eax  
}

  其中下面这一段就是引用部分反汇编之后的内容:

	int a = 1;
00B914AE  mov         dword ptr [a],1  
    int &b = a;
00B914B5  lea         eax,[a]  
00B914B8  mov         dword ptr [b],eax  
  • 其中dword ptr指令的功能是指明内存单元的长度为双字,
  • mov dword ptr [a],1就表示将数字1放到变量a所在的内存处;
  • lea是地址传送指令,即Load Effective Address,指令的格式是lea 目标寄存器 源操作数;是有效地址传送指令,源操作数是内存操作数,将内存单元的有效地址(注意不是内容)传送到目标寄存器中,也就是将目的操作数 的偏移地址送到寄存器中;通过下面这张图也可以加深理解:在这里插入图片描述
    因此lea eax,[a]可以理解为把变量a的地址赋值给寄存器eax;
  • mov dword ptr [b],eax表示把寄存器eax中内容(也就是变量a 的地址)放到变量b所在的内存处;

  如果只是简单的赋值,不使用引用,像下面这样:

#include <iostream>

using namespace std;

int main()
{
    int a = 1;
    //int &b = a;
	int b = a;
    cout << "a: " << a<<endl;
    cout << "b: " << b<<endl;

    cout<<"a 的地址:"<<&a<<endl;
    cout<<"b 的地址:"<<&b<<endl;
    return 0;
}

  输出:

a: 1
b: 1
a 的地址:0x6dfeec
b 的地址:0x6dfee8

  反汇编之后的结果是:

    int a = 1;
00DF14AE  mov         dword ptr [a],1  
    //int &b = a;
	int b = a;
00DF14B5  mov         eax,dword ptr [a]   //把变量a的值赋给了寄存器eax
00DF14B8  mov         dword ptr [b],eax  

  与使用引用之后的结果进行比较我们也能看出来,引用的机理是把一个变量的地址赋给了另一个变量。

	int a = 1;
00B914AE  mov         dword ptr [a],1  
    int &b = a;
00B914B5  lea         eax,[a]  //把变量a的地址赋给了寄存器eax
00B914B8  mov         dword ptr [b],eax  

  经过上边的分析我们就能得出结论:引用的本质就是把一个变量的地址赋给了另一个变量;也就是说,引用本身存放的是被引用对象的地址;有种指针的意味,不过引用和指针是两个概念,不要混淆。

  • 引用只能在定义时初始化一次,之后不能再改变指向其他的变量,但是指针变量的值是可变的;
  • 引用不能为空,必须指向一个有效的变量,但是指针可以置为空(注意指针不要悬挂)
  • sizeof(指针)和sizeof(引用)的意义不一样,sizeof(引用)的结果是所指向的变量的大小,而sizeof(指针)的结果是对象地址的大小。

  另外,这位前辈博客中的例子我觉得非常好,安利一哈:

    引用参数与引用返回值 细节处提升代码效率

  具体的:在类的成员函数中,使用引用参数和引用返回值都不需要产生临时对象,减少了一次对象的拷贝,提高了函数的效率。

  因此,能用引用作为参数时,尽量使用引用,对提升效率有很大的帮助;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值