在一般教材里面,我们会说引用是变量的别名,另外在 c++ primer 5里面说到引用的时候,说引用不是对象,不能对它进行取地址。但是我们来看看下面代码的分析:
1 #include <iostream> 2 3 int main(int argc, char* argv[]) 4 { 5 int i = 90; 6 int &qi = i; 7 int *pqi = &(&qi); 8 int *pi = &i; 9 10 return 0; 11 }
汇编结果:
1 #include <iostream> 2 3 int main(int argc, char* argv[]) 4 { 5 00BC4350 push ebp 6 00BC4351 mov ebp,esp 7 00BC4353 sub esp,0F0h 8 00BC4359 push ebx 9 00BC435A push esi 10 00BC435B push edi 11 00BC435C lea edi,[ebp-0F0h] 12 00BC4362 mov ecx,3Ch 13 00BC4367 mov eax,0CCCCCCCCh 14 00BC436C rep stos dword ptr es:[edi] 15 int i = 90; 16 00BC436E mov dword ptr [i],5Ah // wc: 将90 保存到 i 的内存单元 17 int &qi = i; 18 00BC4375 lea eax,[i] 19 00BC4378 mov dword ptr [qi],eax // wc: 将 i 的地址保存到 qi 的内存单元,这里可以说明引用也是有自己的内存空间的, 20 // wc: 它用来保存所绑定的对象的地址 21 int *pqi = &qi; 22 00BC437B mov eax,dword ptr [qi] 23 00BC437E mov dword ptr [pqi],eax // wc: 这里是将引用取地址给一个指针,我们可以看到,实际上是将 qi 内存单元里的值保存到 pqi 的内存单元, 24 // wc: 那么也就说,对一个引用取地址实际上是对其取值,取出它保存的所绑定对象的地址 25 // wc: 这也就说明了为什么对一个引用取地址得到的是指向其所绑定对象 26 int *pi = &i; 27 00BC4381 lea eax,[i] 28 00BC4384 mov dword ptr [pi],eax // wc: 将 i 的地址保存到 pi 的内存单元,这个是一般的对一个对象取地址 29 30 return 0; 31 00BC4387 xor eax,eax 32 }
这么来说,引用的内部实现可能是常量指针,这样引用就是一个可以被自动解引用的指针。
表达式 int &qi = i; 将会被编译器转化成 int *const qi = &i; 而引用之所以要初始化是因为 const 类型变量必须初始化,这个指针也必须指向。我们还可以以另外一个程序来证明:
1 #include <iostream> 2 using namespace std; 3 4 struct Tq { 5 int &i; 6 double &j; 7 }; 8 9 int main(int argc, char* argv[]) 10 { 11 ┊int *ptr; 12 ┊cout << "size of pointer: " << sizeof(ptr) << endl; 13 ┊cout << "size of Tq: " << sizeof(Tq) << endl; 14 15 ┊ return 0; 16 17 }
运行结果:
size of pointer: 8
size of Tq: 16
可以看出,两个引用其实就是两个指针。而不是像书上说的引用不是对象。
以上的结果都说明,引用其实就是一个指针,基于引用一定要初始化和不能改变绑定,引用的实现应该是 const pointer,它是一个被自动解引用的指针,它的一系列引用操作由编译器来保证。