引用可以简化原来使用指针的代码,至少看起来舒服点,不过今天发现个问题,先来看一段代码
1 #include <iostream> 2 #include <cstdlib> 3 #include <vector> 4 5 using namespace std; 6 7 int main() { 8 vector<int> stack; 9 10 stack.push_back(1); 11 12 int& rx = stack[0]; 13 14 int vx = stack[0]; 15 16 int vy = rx; 17 18 cout<<rx<<endl; 19 20 stack.pop_back(); 21 22 cout<<rx<<endl; 23 24 stack.push_back(2); 25 26 cout<<rx<<endl; 27 28 system("pause"); 29 return 0; 30 }
输出:
1 1 2 |
可以看到引用的值发生了变化,并且在被引用值“不存在”的情况下也返回了一个值。我们说引用的值总是存在的,因此在不会出现NULL的地方且指向不变的地方可以用引用代替指针。实际上引用是一个指针,由于引用使用的语法的规则,它必须有初值(也就是说它不会为NULL),而它的作用域(生命周期)总是等于或小于被引用的变量,而且一旦指定被引用对象后无法改变,因而使用它比直接用指针来的安全。来看一下以上部分代码在VS2012中的反汇编
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
int & rx = stack[0];
00FC6495 push 0 00FC6497 lea ecx,[stack] 00FC649A call std::vector< int ,std::allocator< int > >::operator[] (0FC1276h)
00FC649F mov dword ptr [rx],eax int
vx = stack[0];
00FC64A2 push 0 00FC64A4 lea ecx,[stack] 00FC64A7 call std::vector< int ,std::allocator< int > >::operator[] (0FC1276h)
00FC64AC mov eax,dword ptr [eax] 00FC64AE mov dword ptr [vx],eax int
vy = rx;
00FC64B1 mov eax,dword ptr [rx] 00FC64B4 mov ecx,dword ptr [eax] 00FC64B6 mov dword ptr [vy],ecx |
可以看到调用operator[]返回值存在eax中(函数调用约定),当初始化引用变量rx时,直接将eax给了rx,而当赋值给整型变量vx时,多了一句
1
|
013364AC mov eax,dword ptr [eax] |
即将eax指向的内存内容重新赋值给eax,然后eax再把这个值给vx变量。从这里我们可以看出operator[]返回的eax实际上是一个指针,相应的引用rx也是一个指针,只不过在语法上做了限制,编译器做了检查和额外处理,让它用起来像一个普通值变量。比如语句
1
|
int
vy = rx;
|
将一个引用赋值给一个整型变量,
1. 首先获得指向rx引用对象的指针 (mov eax,dword ptr [rx])
2. 然后根据指针值获得被引用对象的值 (mov ecx,dword ptr [eax])
3. 最后把值赋给变量vy (mov dword ptr [vy],ecx )
再来看一个指针版本的反汇编,就会发现与引用变量相关的汇编代码几乎是一致的。
1
2
3
4
5
6
7
8
|
int * p = &stack[0];
00FC64BB lea ecx,[stack] 00FC64BE call std::vector< int ,std::allocator< int > >::operator[] (0FC1276h)
00FC64C3 mov dword ptr [p],eax int
vz = *p;
00FC64C6 mov eax,dword ptr [p] 00FC64C9 mov ecx,dword ptr [eax] 00FC64CB mov dword ptr [vz],ecx |