C++ 引用
在 C++ 中,“引用”(reference)是一种为已有对象创建别名(alias)的机制。它在语法和使用上都比指针更安全、简洁,被广泛用于函数参数传递和返回值优化。下面分几个方面介绍它的概念与用法。
1. 引用的基本概念与语法
int x = 10;
int& rx = x; // 声明一个 int 的引用 rx,必须立即初始化
int& rx
:声明了一个引用类型,rx
与x
共享同一块内存。- 引用与原对象是同一实体,无需解引用操作,使用时就像操作原变量一样:
rx = 20; // 相当于 x = 20 std::cout << x; // 输出 20
- 必须初始化:引用在声明时就要绑定到某个对象,且之后不可更改绑定关系。
2. 引用与指针的对比
特性 | 引用 (T& ) | 指针 (T* ) |
---|---|---|
是否可为空 | 不可为空 | 可指向 nullptr |
是否可改变绑定 | 不可改变绑定 | 可通过赋值改变指向 |
语法及使用 | 像普通变量,无需解引用操作 | 访问需解引用 *p |
安全性 | 更安全,避免野指针 | 易出空指针、野指针 |
3. 函数参数中的引用
3.1 传递引用以避免拷贝
void print(const std::string& s) {
std::cout << s << "\n";
}
const T&
:最常用的参数传递方式,既避免了拷贝,又保证了函数内不修改原对象。- 若不加
const
,传入的左值可以被修改:void increment(int& n) { ++n; } int a = 1; increment(a); // a 变为 2
3.2 右值引用(C++11)
void process(std::string&& s) {
// 可以“窃取”s的资源,避免拷贝
std::string local = std::move(s);
}
T&&
:绑定到右值(临时对象),用于实现移动语义。- 配合
std::move
、std::forward
,可实现“完美转发”和资源的高效移动。
4. 函数返回引用
4.1 返回对象的引用
int& getElement(std::vector<int>& v, size_t i) {
return v[i];
}
std::vector<int> vec = {1,2,3};
getElement(vec, 1) = 42; // 直接修改 vec[1]
- 注意:不可返回对临时对象或局部变量的引用,否则会产生悬空引用(undefined behavior)。
4.2 返回右值引用
std::string&& makeString() {
return std::move(std::string("temp"));
}
- 用于库内部实现,用户通常更关心移动构造和返回值优化(RVO)。
5. 常量引用与非常量引用
- 非常量引用(
T&
)只能绑定到左值(可修改对象)。 - 常量引用(
const T&
)可绑定到左值、右值和字面量,保证函数体内不修改引用对象:void foo(const int& x); foo(5); // 合法,绑定到临时右值
6. 引用折叠与模板中的转发引用
在模板场景下,T&&
不总是右值引用,若 T
是一个引用类型,则会发生“引用折叠”规则:
形式 | 折叠结果 |
---|---|
T& & | T& |
T& && | T& |
T&& & | T& |
T&& && | T&& |
- 转发引用(又名万能引用):当函数模板参数声明为
template<class T> void f(T&&);
时,根据传入实参是左值或右值,T&&
会折叠成T&
或T&&
,配合std::forward<T>
可完美转发参数。
7. 引用的常见误区
- 不能将引用重新指向另一个对象
int x = 1, y = 2; int& r = x; // r = y; // 这只是把 y 的值赋给 x,而不是让 r 绑定到 y
- 避免对
nullptr
、已析构对象使用引用:引用本身不可为空,但可通过const T&
绑定到短生命周期的临时对象,临时对象销毁后引用失效。 - 不要返回对局部变量的引用,否则会出现悬空引用。
8. 何时使用引用
- 函数参数:若要修改参数或避免拷贝且不需要空值,使用
T&
/const T&
;如果需要接受右值以实现移动,则T&&
。 - 函数返回:若需链式调用或提供对内部元素的直接访问,且返回对象生命周期安全,可返回
T&
;否则尽量返回值或智能指针。 - 替代指针:在接口中,如果不需要空值,引用更直观;若可能指向“无对象”,必须使用指针或可空类型(如
std::optional<std::reference_wrapper<T>>
)。
小结
- 引用是对象的别名,语法简洁、安全,适用于绝大多数接口设计。
- C++11 引入了右值引用,为移动语义和完美转发提供了强大支持。
- 理解引用折叠与转发引用是掌握现代模板库(如
<utility>
、<algorithm>
)的关键。