关于引用可以被重新赋值的搞笑误解

以前在群里面发现有人说,引用可以被赋值,然后其推断出引用其实是可以重新指向其它对象的,而不是只能在初始化时确定所指对象,以后不能改变。如果是这样的话,引用和指针还有什么区别了。存在引用的好处就是,引用一经过定义就确定所指对象,以后不用测试其是否指向无效对象,也不用担心所指对象会改变。
下面来看看这段造成误解的代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;

int main()
{
int nA = 10;
int nRef = nA;//必须初始化

cout << "nA:" << nA << ' ' << "nRef:" << nRef << endl;

int nB = 11;
nRef = nB;//难道引用可以重新指向nB?
cout << "nB:" << nB << endl;
cout << "nA:" << nA << ' ' << "nRef:" << nRef << endl;

nRef = 12;
cout << "nB:" << nB << endl;
cout << "nA:" << nA << ' ' << "nRef:" << nRef << endl;

return 0;
}

运行结果:
从结果可以看到,用nB对nRef进行赋值后,nA的值也变成11了,由此可见nRef仍然指向nA。而再对nRef赋值12,只有nA变成12,而nB仍然是11。这些都可以很清楚的说明,引用所指的对象是没有变化的,一经初始化就不会改变。
下面再来看看,注释掉输出语句后的反汇编代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int main()
{
00EC48F0 push ebp
00EC48F1 mov ebp,esp
00EC48F3 sub esp,0E4h
00EC48F9 push ebx
00EC48FA push esi
00EC48FB push edi
00EC48FC lea edi,[ebp-0E4h]
00EC4902 mov ecx,39h
00EC4907 mov eax,0CCCCCCCCh
00EC490C rep stos dword ptr es:[edi]
int nA = 10;
00EC490E mov dword ptr [nA],0Ah
int nRef = nA;//必须初始化
00EC4915 lea eax,[nA]
00EC4918 mov dword ptr [nRef],eax

//cout << "nA:" << nA << ' ' << "nRef:" << nRef << endl;

int nB = 11;
00EC491B mov dword ptr [nB],0Bh
nRef = nB;//难道引用可以重新指向nB?
00EC4922 mov eax,dword ptr [nRef]
00EC4925 mov ecx,dword ptr [nB]
00EC4928 mov dword ptr [eax],ecx
//cout << "nB:" << nB << endl;
//cout << "nA:" << nA << ' ' << "nRef:" << nRef << endl;

nRef = 12;
00EC492A mov eax,dword ptr [nRef]
00EC492D mov dword ptr [eax],0Ch
//cout << "nB:" << nB << endl;
//cout << "nA:" << nA << ' ' << "nRef:" << nRef << endl;

return 0;
00EC4933 xor eax,eax
}

从以下几句能看到nRef实际上存储的是nA的地址。lea是取有效地址的意思。

1
2
3
int nRef = nA;//必须初始化
00EC4915 lea eax,[nA]
00EC4918 mov dword ptr [nRef],eax

从给引用赋值的汇编代码可以看出,实际上的动作是把nRef代表的地址放入一个寄存器,再取nB的值放入一个寄存器,然后将nB的值赋给nRef指向的内存。

1
2
3
4
nRef = nB;//难道引用可以重新指向nB?
00EC4922 mov eax,dword ptr [nRef]
00EC4925 mov ecx,dword ptr [nB]
00EC4928 mov dword ptr [eax],ecx