引用(reference)是已存在对象的别名:声明时必须绑定到对象,之后对引用的操作即对原对象的操作。引用不可重新绑定到其他对象,且不能为空(左值引用语义)。C++ 常用引用传递大对象或需要修改形参的场景,语法比指针更简洁。本节在指针基础上说明左值引用、const 引用及与指针的差异。
引用的含义
若 int& ref = n;,则 ref 与 n 表示同一存储单元,不占用额外空间保存副本(实现上可能用地址,但语言层面是别名)。未初始化的引用非法,编译器会报错。
基本用法
#include <iostream>
int main() {
int n = 5;
int& ref = n;
ref = 10;
std::cout << n << "\n";
return 0;
}典型输出:10。对 ref 赋值即修改 n。
引用与指针的语法对比
| 操作 | 引用 | 指针 |
|---|---|---|
| 声明并绑定 | int& r = x; | int* p = &x; |
| 访问对象 | r | *p |
| 重新绑定 | 不允许 | p = &y; 允许 |
| 空值 | 无 | nullptr |
引用形参
按引用传递可避免拷贝大对象,并允许函数修改实参:
#include <iostream>
void swap_int(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
int main() {
int x = 1, y = 2;
swap_int(x, y);
std::cout << x << " " << y << "\n";
return 0;
}典型输出:2 1。调用处写法与按值传递相同,无需写 & 或 *。
const 引用
const T& 承诺不通过该引用修改对象,可绑定到临时量并延长其生命周期至引用所在作用域结束(延长规则仅适用于 const 引用绑定临时量,不宜依赖其做复杂设计):
void print(const std::string& s) {
std::cout << s.size() << "\n";
}
int main() {
print("hello"); // 临时 string 可绑定到 const 引用
return 0;
}只读遍历容器时常写 const auto&,见范围 for。需要修改时使用非 const 引用。
引用与指针对比
| 特性 | 引用 | 指针 |
|---|---|---|
| 是否可为空 | 否(std::reference_wrapper 等属特例) | 可 nullptr |
| 重新绑定 | 否 | 可改指向 |
| 语法 | 与对象相同 | 需 *、-> |
| 多级 | 无 int&& 左值引用叠用 | 可有 int** |
| sizeof | 得到被引用对象类型的大小 | 得到指针本身大小 |
返回引用
函数可返回引用,但不得返回局部变量的引用或指针:函数结束后局部对象销毁,引用悬垂。合法情形包括:返回参数引用、静态存储期对象、或类成员引用。
int& bad() {
int local = 0;
return local; // 悬垂引用,未定义行为
}返回容器元素引用时,须保证容器在调用方使用期间仍然存活,例如由调用方拥有的 std::vector。
引用与指针的选用
- 默认传参:大对象或不可拷贝类型用
const T&;需要可选参数或重绑定用指针并判空。 - 与 C 接口:C API 多用指针;C++ 封装层可将引用转为内部指针。
- 成员:类可含引用成员,但须在构造函数初始化列表中绑定,且对象寿命须覆盖引用所绑对象。
右值引用(预告)
C++11 引入右值引用 T&&,用于移动语义与完美转发,见移动语义。本节左值引用是日常传参与别名的基础。
本节说明了引用的别名语义、const 引用与参数传递。下一节介绍new 与 delete:在自由存储区分配对象。