引用
引用的概念和定义
引用不是新定义的变量,而是给已存在变量取一个别名。编译器不会为引用开辟新的内存空间,它和原变量共用同一块内存。
类型&引用别名=引用对象
为了避免引入太多运算符,C++ 复用了一些符号。比如引用和取地址都使用 &,使用时要注意区分上下文。创建变量 i 时开辟了一块空间,int& j = i 就是给这块空间又取了个名字叫 j,还可以继续取别名 k。
引用可以给一个变量取多个别名,也可以给别名再取别名。
引用的特性
- 引用在定义时必须初始化(必须明确是谁的别名)
- 一个变量可以有多个引用
- 引用一旦绑定一个实体,就不能再引用其他实体
如果 k 已经是 i 和 j 的别名了,就不能再指向实体 m 了。此时 k=m 实际上是赋值操作,而不是重新绑定。
引用的使用
C++ 中引用主要是为了解决指针在某些场景下的不足。大部分情况下可以用引用替代指针,但部分底层场景仍离不开指针。
引用传参的使用
之前完成 x 和 y 的交换通常用指针,现在也可以用引用平替。
void swap(int &rx, int &ry) {
int temp = rx;
rx = ry;
ry = temp;
}
rx 和 ry 是 x 和 y 的别名,交换它们就是交换原变量。这里引用看似没有显式初始化,其实是在函数调用时隐式绑定的。
指针交换和引用交换可以共存,在 C++ 中这构成了函数重载。
在数据结构操作中也可以直接使用引用。例如链表尾插,如果不使用引用,可能需要二级指针,逻辑比较绕。使用引用后,形参直接成为实参的别名,修改形参即修改实参。
学校教材有时为了避开二级指针会写得比较复杂,用了 C++ 引用语法反而可能让代码更繁琐。引用不能完全替代指针,比如在链表、树的节点定义中,节点间物理空间不连续,必须用指针存储地址。树和链表的操作常需要改变指向,而 C++ 的引用无法改变指向(一旦绑定不能变),这是指针的核心优势之一。
关于返回问题,传值返回会产生临时变量。函数结束后局部变量销毁,返回的是拷贝的临时对象。一般小对象存寄存器,大对象在栈帧中开辟空间。也不能对返回值直接进行加等运算,因为返回的是临时对象的拷贝,临时对象具有常性,不可修改。
传引用返回的错误使用
传引用返回返回的是变量的别名。如果返回局部变量的引用,是非常危险的。
int& func() {
int tmp = 10;
return tmp; // 错误:tmp 是局部变量,函数结束会被销毁
}
函数结束后,栈帧销毁,tmp 的空间被释放。虽然还能访问到旧数据,但这属于野指针行为,读取的值可能是随机的,甚至导致程序崩溃。VS 上可能显示 0 并报警告,越界读不一定报错,但越界写一定会。


