先谈引用
由于C中的指针过于灵活,容易出错,所以C++引入了引用来减少出错的用法,记得C++ primer中说,引用就是一个变量的别名,在初始化引用后,引用就无法改变再引用到其他的对象了。在遇到类的移动构造函数时,又见识到了右值引用,书上说,右值引用用于引用到临时对象。
那么就存在一些问题了:
- 这个别名到底是怎么实现的呢?
- 引用和指针存在什么关系呢?
- 左值引用和右值引用本质上的区别在哪?
看看汇编实现
对于这些问题,在C++语言层面上,还是难以把握,我们看看汇编层面是如何实现的:
观察上图可以发现,原来引用也是一个变量,其跟指针的行为竟然一样一样的!
可以看到d
引用到c
,和把c
的地址放到指针ptr2
中,其操作一模一样,这也就回答了前两个问题:
引用的本质其实就是指针,但在C++中对于这个特殊的指针,做了诸多的限制,在初始化后,就不能再指向其他的对象,既然不能指向其他对象,那么也就没有必要取指针内部的地址了,那么使用引用的时候,其默认就是取内部地址所指向的对象的值,就与直接使用对象的变量名一样。
再观察一下ptr1
,可以发现,ptr1
是取引用d的地址,然而汇编实现时,是把d的值传给了ptr1
,即ptr1
指向了c
,所以对引用取地址,其实等于引用的对象的地址。
再来看看右值引用,可以发现右值引用一个很大的特点在于:它可以引用到”寄存器中的值”,当然不真的引用到寄存器,而是它会先把寄存器中的值保存在内存中,然后再引用到它。这个临时开辟的空间并没有像普通变量那样有个变量名,它只有这个引用来指向它。
如图中的,y
,g
都属于这种情况,这种情况用左值引用是无法办到的。
函数传值
观察上图,可以发现:
在第一次func1
中,传入左值对象,调用了拷贝构造函数,生成了临时对象a
,然后构造b
对象,返回时,由于a
对象为临时对象,将调用返回的临时对象的移动构造函数窃取a
的资源,最后,析构顺序为,b
局部变量,a
临时变量,返回的临时变量。
当传入右值对象时,此时生成的临时对象a
将调用移动构造函数,窃取传入的右值对象的资源,后续步骤与上类似。
再看func2
,由于a
是引用对象,所以返回的值对象时调用的拷贝构造函数。
当返回的对象被引用,此时返回的临时对象不会被析构。
再看func3
,可以发现,即便以左值引用传递进来,当返回它时,仍然调用的是拷贝构造函数,而func4
中,返回局部变量是调用的移动构造函数。
个人认为,对于右值引用,在分析时,应该看具体情况,其本质是为了直接窃取不用的资源,避免重复拷贝构造。