在 C++ 中,引用(reference) 是某个已存在对象的别名。一旦定义了引用,对该引用的任何操作都作用在被引用的对象上;因此,我们可以通过引用读取或修改原对象。
初看之下,引用似乎多余,但在 C++ 中它们无处不在,稍后几课将展示其强大用途。
关键要点
引用在语义上与其所引用的对象完全一致。
(进阶提示)
也可以创建对函数的引用,只是这种做法较少见。
现代 C++ 提供两种引用:
- 左值引用(lvalue reference)
- 右值引用(rvalue reference)
本章仅讨论左值引用;右值引用将在第 22 章“移动语义”中介绍。
左值引用类型
左值引用(常简称“引用”)是某个左值对象(如变量)的别名。
其类型由被引用类型后加一个 &
标识:
int // 普通 int,非引用
int& // 指向 int 的左值引用
double& // 指向 double 的左值引用
const int& // 指向 const int 的左值引用
int&
表示“指向 int 的左值引用”;const int&
表示“指向 const int 的左值引用”。
术语
- 引用类型:带
&
的完整类型(如int&
)。 - 被引用类型:去掉
&
后的类型(如int
)。
左值引用分为两种:
- 非常量左值引用:可修改原对象。
- 常量左值引用:只读原对象(下一课 12.4 讨论)。
左值引用变量
定义语法:在变量名前加 &
:
int x{ 5 }; // 普通整型
int& ref{ x }; // ref 是 x 的左值引用
示例程序:
#include <iostream>
int main()
{
int x{ 5 };
int& ref{ x };
std::cout << x << '\n'; // 5
std::cout << ref << '\n'; // 5
}
输出:
5
5
风格建议
现代 C++ 社区倾向把 &
写在类型侧:int& ref
,以突出“引用”是类型的一部分,而非标识符的一部分。
通过非常量左值引用修改原对象
#include <iostream>
int main()
{
int x{ 5 };
int& ref{ x };
std::cout << x << ref << '\n'; // 55
x = 6; // 通过 x 修改
std::cout << x << ref << '\n'; // 66
ref = 7; // 通过 ref 修改
std::cout << x << ref << '\n'; // 77
}
输出:
55
66
77
引用初始化
- 必须初始化;未初始化即报错。
- 初始化过程称为引用绑定(reference binding),被引用对象称referent。
int& invalidRef; // 错误:未初始化
int x{ 5 };
int& ref{ x }; // 合法:绑定到 x
非常量左值引用只能绑定到可修改左值:
int x{ 5 };
const int y{ 5 };
int& ref1{ x }; // OK
int& ref2{ y }; // 错误:不能绑定到 const 左值
int& ref3{ 0 }; // 错误:不能绑定到右值
(如果允许绑定到 const 或右值,则可通过引用修改其值,违反 const 语义。)
- 不允许
void&
(无意义)。 - 类型不匹配时,编译器尝试隐式转换;若转换结果为右值,则非常量左值引用无法绑定,导致编译错误。
引用不可重定向
引用一经绑定,终身不可改指。
int x{ 5 }, y{ 6 };
int& ref{ x };
ref = y; // 把 y 的值赋给 x,而非让 ref 指向 y
作用域与生命周期
引用变量遵循与普通变量相同的作用域与生命周期规则;引用与其 referent 的生命周期相互独立:
int main()
{
int x{ 5 };
{
int& ref{ x };
std::cout << ref << '\n'; // 5
} // ref 销毁,x 不受影响
std::cout << x << '\n'; // 5
}
悬垂引用(dangling reference)
若 referent 先于引用被销毁,则引用悬垂,访问它产生未定义行为。我们将在 12.12 课“返回引用与返回地址”中展示实际场景。
引用不是对象
- 引用本身不是对象,不保证占用存储;编译器常将其优化掉。
- 因此“引用变量”一词并不严谨——变量是具名对象,而引用不是。
- 不能声明“引用的引用”,也不能把引用当作对象使用;若需可重定向的引用对象,可用
std::reference_wrapper
(第 23.3 课)。
示例:
int var{};
int& ref1{ var }; // ref1 绑定 var
int& ref2{ ref1 }; // ref2 同样绑定 var(而非“引用的引用”)
int&&
在 C++11 起表示右值引用,而非“引用的引用”。
测验
问题 1
不编译运行,推断下面程序输出:
#include <iostream>
int main()
{
int x{ 1 };
int& ref{ x };
std::cout << x << ref << '\n'; // 11
int y{ 2 };
ref = y; // 把 2 赋给 x
y = 3; // 仅改 y
std::cout << x << ref << '\n'; // 22
x = 4; // 同时改 x 与 ref
std::cout << x << ref << '\n'; // 44
}