653 字
2 分钟
【CPP】引用与值类别
2026-03-18

整理自 0318.md,已校对并补充移动语义入门。

左值与右值#

类别特征示例
左值 (lvalue)有身份、可取地址、可出现在赋值号左侧(在合适类型下)变量、数组元素、返回左值引用的函数
右值 (rvalue)临时对象、字面量、即将销毁的值42std::move(x) 的结果、返回值的临时对象

C++11 进一步细分:

  • 纯右值 (prvalue):如 42std::string("hi") 的临时结果。
  • 将亡值 (xvalue):通过 std::move 或即将被移动的资源。
  • 左值 (lvalue) / 亡值 (glvalue):统称“有身份”的表达式。

日常口诀:能取地址的通常是左值;字面量和临时量是右值。

左值引用#

已存在对象的别名,不是指针

int a = 10;
int& ref = a; // 必须初始化
ref = 20; // 修改 ref 即修改 a

规则

  1. 声明时必须初始化。
  2. 初始化后不能改绑到其他对象(指针可以改指向)。
  3. 不存在“空引用”,必须绑定到合法对象。
  4. 传参用引用可避免拷贝(对大对象尤其重要)。

引用与指针的区别#

引用指针
空值不允许允许 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; // OK
const std::string& s = std::string("tmp"); // 临时对象生命期延长至 s 所在作用域结束

典型用途

  1. 只读传参,避免拷贝:void f(const std::string& name);
  2. 接受字面量或临时量。
  3. 接口中表达“不修改实参”的约定。

补充:万能引用与转发(C++11)#

template<typename T>
void wrapper(T&& arg) { // 转发引用(原称万能引用)
process(std::forward<T>(arg));
}

T&& 在模板推导中可能是左值引用或右值引用,需配合 std::forward 保持值类别。初学阶段知道存在即可,与 std::move 成对学习。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

【CPP】引用与值类别
https://lysj.work/posts/studynotes/cpp/cpp引用与值类别/
作者
Sekiro
发布于
2026-03-18
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录