一、左值引用
比如:
int a = 123;
int &b = a; // 定义一个左值引用变量
b = 456; // 通过左值引用修改引用内存的值
如何理解左值引用?
- 人的视角
- 指针或别名
- 比一串地址的可读性好,其实就是一回事
- 编译器视角
- 地址
- 即:CPU可以对其执行寻址并读写数据,如内存地址
- 编译后会被转换为明确的地址
- 示例1
- 比如:int &a = 10
这种定义是无法通过编译的。
因为10是立即数,其值在代码段里(不在数据段,所以没有地址)
计算过程中,会直接被读取到寄存器中参与运算,没有内存地址。
而没有内存地址,意味着编译器碰到这句话,将无法用引用替换别名a
- 示例2
- 比如:const int &a = 10
这种定义是ok的。
因为常量在静态存储区,加载的时候就会在内存中分配其地址。
二、右值引用
- 解决常量引用只能读取,不能修改的问题
如何理解右值引用?
- 定义(非官方、约定俗成)
- 左值:可以取地址的,有名字的,非临时的
- 右值:不能取地址的,没有名字的,临时的
- 本质(理解)
- 创建和销毁由编译器幕后控制,程序员只能确保在本行代码有效的,就是右值
- 比如,立即数,函数返回的值等都是右值
- 由用户创建,通过作用域规则确定生命周期的,就是左值
- 比如,非匿名对象或变量,函数返回的引用,const对象等
- 格式
- 类型 && 引用名 = 右值表达式;
- 说明:
- 右值引用是C++ 11新增的特性,所以C++ 98的引用为左值引用
- 右值引用用来绑定到右值
- 绑定到右值以后本来会被销毁的右值的生命周期会延长至与绑定到它的右值引用的生命周期
- 右值引用的存在并不是为了取代左值引用,而是充分利用右值(特别是临时对象)的构造来减少对象构造和析构操作以达到提高效率的目的
- 移动构造函数
- 带右值引用参数的拷贝构造和赋值重载函数,又叫移动构造函数和移动赋值函数
- 这里的移动指的是把临时量的资源移动给了当前对象,临时对象就不持有资源,为nullptr了
- 实际上没有进行任何的数据移动,没发生任何的内存开辟和数据拷贝