653 字
2 分钟
【CPP】引用与值类别
整理自 0318.md,已校对并补充移动语义入门。
左值与右值
| 类别 | 特征 | 示例 |
|---|---|---|
| 左值 (lvalue) | 有身份、可取地址、可出现在赋值号左侧(在合适类型下) | 变量、数组元素、返回左值引用的函数 |
| 右值 (rvalue) | 临时对象、字面量、即将销毁的值 | 42、std::move(x) 的结果、返回值的临时对象 |
C++11 进一步细分:
- 纯右值 (prvalue):如
42、std::string("hi")的临时结果。 - 将亡值 (xvalue):通过
std::move或即将被移动的资源。 - 左值 (lvalue) / 亡值 (glvalue):统称“有身份”的表达式。
日常口诀:能取地址的通常是左值;字面量和临时量是右值。
左值引用
已存在对象的别名,不是指针。
int a = 10;int& ref = a; // 必须初始化ref = 20; // 修改 ref 即修改 a规则:
- 声明时必须初始化。
- 初始化后不能改绑到其他对象(指针可以改指向)。
- 不存在“空引用”,必须绑定到合法对象。
- 传参用引用可避免拷贝(对大对象尤其重要)。
引用与指针的区别
| 引用 | 指针 | |
|---|---|---|
| 空值 | 不允许 | 允许 nullptr |
| 重新绑定 | 不允许 | 允许 |
| 语法 | 像别名,无需解引用 | 需 * / -> |
| sizeof | 不占额外空间(实现上常是指针) | 占指针大小 |
各种类型的引用
int x = 10;int& r = x;
int arr[5] = {1,2,3,4,5};int (&r_arr)[5] = arr; // 数组引用
int* p = &x;int*& r_p = p; // 指针的引用r_p = &y; // 修改的是 p 本身,不是“引用改绑”返回引用
禁止返回局部变量的引用(悬垂引用):
int& bad() { int local = 1; return local; // 未定义行为}可以返回:
- 静态或全局对象
- 成员变量(
return *this链式调用) - 由参数传入的对象的引用
class Builder { int value = 0;public: Builder& set(int v) { value = v; return *this; }};// Builder().set(1).set(2); // 注意:临时对象的链式调用生命期极短,易出错右值引用
绑定到右值,为移动语义服务:
int&& r = 42; // 绑定到字面量std::string&& s = std::string("hello");
int x = 10;int&& rr = std::move(x); // 显式将左值转为右值引用- 右值引用变量本身是左值(有名字就能取地址),若要继续传递为右值,需再
std::move。 - 移动构造函数 / 移动赋值接受右值引用,可“窃取”资源而非深拷贝。
常量引用 const T&
- 不能通过该引用修改对象(除非原对象本身可变且通过其他途径修改)。
- 可绑定到右值,延长临时对象生命期(绑定到
const左值引用时):
// int& r = 90; // 错误:非 const 左值引用不能绑右值const int& r = 90; // OKconst std::string& s = std::string("tmp"); // 临时对象生命期延长至 s 所在作用域结束典型用途:
- 只读传参,避免拷贝:
void f(const std::string& name); - 接受字面量或临时量。
- 接口中表达“不修改实参”的约定。
补充:万能引用与转发(C++11)
template<typename T>void wrapper(T&& arg) { // 转发引用(原称万能引用) process(std::forward<T>(arg));}T&& 在模板推导中可能是左值引用或右值引用,需配合 std::forward 保持值类别。初学阶段知道存在即可,与 std::move 成对学习。
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐
