引用就是对象的另一个名字--这是《c++primer》中介绍引用里的一句话;而且很多c++参考书中都有“引用是另一个对象的别名”这一说;这句话很容易让人引起误会。看下面的例子:
int main()
{
int a;
int &b = a;
a = 1;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
b = 2;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"&a="<<&a<<endl;
cout<<"&b="<<&b<<endl;
return 0;
}
输出结果:
a=1
b=1
a=2
b=2
&a=0012FF7C
&b=0012FF7C
从上面看出,对a操作相当于对b操作,对b操作相当于对a操作,就好比b是a的影子,两者都会互相影响,但是影子和实物本质上还是有区别的,看如下会编代码:
1015: int a;
1016: int &b = a;
00401598 lea eax,[ebp-4]
0040159B mov dword ptr [ebp-8],eax
这几行汇编代码中,先得到b(ebp-4)的地址赋给eax,然后eax赋给ebp-8(也就是a)。反正就是把b的地址赋给a,最后a里面存的是b的地址,而b里面呢,是数值(因为没初始化所以是一个int型的随机数)。到这里,你还认为a和b是一样的吗?
再看看下面的:
1026: cout<<"&a="<<&a<<endl;
00401656 push offset @ILT+200(std::endl) (004010cd)
0040165B lea edx,[ebp-4] ;获取a的地址
0040165E push edx
0040165F push offset string "&a=" (0046f020)
00401664 push offset std::cout (0047ce90)
00401669 call @ILT+650(std::operator<<) (0040128f)
0040166E add esp,8
00401671 mov ecx,eax
00401673 call @ILT+60(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401041)
00401678 mov ecx,eax
0040167A call @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)
1027: cout<<"&b="<<&b<<endl;
0040167F push offset @ILT+200(std::endl) (004010cd)
00401684 mov eax,dword ptr [ebp-8] ;&b操作,想想这里为什么不是lea eax [ebp -8]呢,因为这里b是对a的引用
00401687 push eax
00401688 push offset string "&b=" (0046f01c)
0040168D push offset std::cout (0047ce90)
00401692 call @ILT+650(std::operator<<) (0040128f)
00401697 add esp,8
0040169A mov ecx,eax
0040169C call @ILT+60(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401041)
004016A1 mov ecx,eax
004016A3 call @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)
从这段代码的结果看出&a和&b操作都是反回a的地址,但是过程不同,对于&a,它是lea edx,[ebp-4];而&b,它是eax,dword ptr [ebp-8] 也就是b的值给eax。说明b存的是a的地址
1018: a = 1;
0040159E mov dword ptr [ebp-4],1 ;对a的赋值
1019: cout<<"a="<<a<<endl;
004015A5 push offset @ILT+200(std::endl) (004010cd)
004015AA mov ecx,dword ptr [ebp-4] ;对a的读取
004015AD push ecx
004015AE push offset string "a=" (0046f028)
004015B3 push offset std::cout (0047ce90)
004015B8 call @ILT+650(std::operator<<) (0040128f)
004015BD add esp,8
004015C0 mov ecx,eax
004015C2 call @ILT+255(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401104)
004015C7 mov ecx,eax
004015C9 call @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)
1020: cout<<"b="<<b<<endl;
004015CE push offset @ILT+200(std::endl) (004010cd)
004015D3 mov edx,dword ptr [ebp-8] ; 对b的读取代码
004015D6 mov eax,dword ptr [edx]
004015D8 push eax
004015D9 push offset string "b=" (0046f024)
004015DE push offset std::cout (0047ce90)
004015E3 call @ILT+650(std::operator<<) (0040128f)
004015E8 add esp,8
004015EB mov ecx,eax
004015ED call @ILT+255(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401104)
004015F2 mov ecx,eax
004015F4 call @ILT+480(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e5)
1021:
1022: b = 2;
004015F9 mov ecx,dword ptr [ebp-8] ;对b赋值
004015FC mov dword ptr [ecx],2
从中看出对a的赋值代码,和对a的读取代码都是直接操作。而对b的读取和赋值操作中,先读取b中保存的值(a的地址)给edx,然后通过dword ptr [edx]完成读取,对于赋值也一样;
所以,对于a的赋值和读取操作,直接就可以;而对于b(它是引用),先要读取它里面的保存的值(也就是a的地址),然后根据这个地址进行读取和赋值操作。当然我们写代码时,完全可以把b当成a的一个别名来操作;
------本人菜鸟一个,有什么地方理解错了,望大牛指出