5.引用
说到引用,我们第一反应就是想到了他的兄弟:指针。引用从底层来说和指针就是同一个东西,但是在编译器中它的特性和指针完全不同。
int a = 10;
int &b = a;
int *p = &a;
//b = 20;
//*p = 20;
首先定义一个变量a = 10,然后我们分别定义一个引用b以及一个指针p指向a。我们来转到反汇编看看底层的实现:
可以看到底层实现完全一致,取a的地址放入eax寄存器,再将eax中的值存入引用b/指针p的内存中。至此我们可以说(在底层)引用本质就是一个指针。
了解了底层实现,我们回到编译器。我们看到对a的值的修改,指针p的做法是*p = 20;即进行解引用后替换值。底层实现:
再来看看引用修改:
我们看到修改a的值的方法也是一样的,也是解引用。只是我们在调用的时候有所不同:调用p时需要*p解引用,b则直接使用就可以。由此我们 推断出:引用在直接使用时是指针解引用。p直接使用则是它自己的地址。
这样我们也了解了,我们给引用开辟的这块内存是根本访问不到的。如果直接用就直接解引用了。即使打印&b,输出的也是a的地址。
在此附上将指针转为引用的小技巧:int *p = &a,我们将 引用符号移到左边 将 *替换即可:int &p = a。
接下来看看如何创建数组的引用:
int array[10] = {0}; //定义一个数组
我们知道,array拿出来使用的话就是数组array的首元素地址。即是int *类型。
那么&array是什么意思呢?int **类型,用来指向array[0]地址的一个地址吗?不要想当然了,&array是整个数组类型。
那么要定义一个数组引用,按照上面的小诀窍,先来写写数组指针吧:
int (*q) [10] = &array;
将右侧的&对左边的*进行覆盖:
int (&q)[10] = array;
测试sizeof(q) = 10。我们成功创建了数组引用。
经过上面的详解 ,我们知道了引用其实就是取地址。那么我们都知道一个立即数是没有地址的,即
int &b = 10;
这样的代码是无法通过编译的。那如果你就是非要引用一个立即数,其实也不是没有办法:
const int &b = 10;
即将这个立即数用const修饰一下,就可以了。为什么呢?
这时因为被const修饰的都会产生一个临时量来保存这个数据,自然就有地址可取了。
总结:引用底层就是指针,使用时会直接解引用,可以配合const对一个立即数进行引用。