34.引用-左值引用和右值引用
34.1 知识点
左值和右值的含义
左值和右值是表达式结果的分类,主要用于区分表达式的特性,如持久性和是否具有存储位置。它们帮助我们理解 C++ 如何管理对象的生命周期和内存使用。
左值
左值是在表达式结束后仍然存在的对象,具有持久性,通常可以被赋值。
特点如下:
- 有明确的存储位置,可以通过取地址运算符
&
获取地址。 - 可以出现在赋值运算符
=
的左边,因为它代表的是可修改的对象。
示例:
int x = 5; // x 是左值,因为它有存储位置,可以被修改
// 输出左值 x 的地址,证明它有存储位置
std::cout << "x 的地址: " << &x << std::endl;
x = 10; // 可以给左值 x 赋值,因为它是持久存在的
int* p = &x; // 可以获取左值 x 的地址并存储在指针 p 中
右值
右值是临时对象或不具持久性的值,一般在表达式计算完成后即被释放。
特点如下:
- 通常没有明确的存储位置,不能直接使用取地址运算符
&
获取地址。 - 右值表达式一般只能出现在赋值运算符
=
的右边,因为它是临时的。 - 右值通常是临时计算的结果,或者没有名称的值,比如字面值、临时对象等。
示例:
int y = 5 + 10; // 这里 5 + 10 的结果是一个右值,它是临时的,计算完成后就不再保留
// 以下代码会导致编译错误,因为无法获取右值的地址
// int* p2 = &(5 + 10);
简单记忆左右值的区别
- 左值:有名称、持久存储在内存中的对象,可以被持续引用和修改。
- 右值:在计算过程中产生的临时值,用完即弃,不具有持久性。
左值引用
左值引用的定义
我们之前学习的引用(使用 &
符号声明)是左值引用。左值引用只能绑定到左值,即绑定到持久存储在内存中的变量或对象。
示例:
int a = 10;
int& refA = a; // 这是一个左值引用,refA 是 a 的别名
左值引用的限制
- 左值引用不能绑定到右值。
以下代码会导致编译错误:
// int& refB = 15; // 15 是右值,不能被左值引用绑定
// int& refC = getInt(); // getInt() 的返回值是右值,不能被左值引用绑定
原因:左值引用需要一个持久的存储位置,而右值是临时的。
右值引用
右值引用的定义
右值引用使用 &&
符号声明,右值引用只能绑定到右值,如函数返回值、字面量等临时对象。
右值引用的特点
- 可以绑定到其他右值。
- 延长了右值的生命周期,使其在引用的作用域内持续存在。
示例
int&& refB = 15; // 这是一个右值引用,refB 绑定到右值 15
std::cout << "refB 绑定到 15 的值: " << refB << std::endl; // 输出右值引用 refB 的值
refB = getInt(); // 可以将右值引用绑定到另一个右值(getInt() 的返回值)
std::cout << "refB 绑定到 getInt() 的值: " << refB << std::endl; // 输出更新后的 refB 的值
注意事项
右值引用的意义在当前阶段可能不太明显,但在后续学习中会变得重要,例如在移动语义和完美转发中。
34.2 知识点代码
Lesson34_引用_左值引用和右值引用.cpp
#include <iostream>
using namespace std;
// 函数声明,用于返回一个整数
int getInt();
int main()
{
std::cout << "左值引用和右值引用\n";
#pragma region 知识点一 左值和右值的含义
// 左值和右值是表达式结果的分类,主要用于区分表达式的特性,如持久性和是否具有存储位置
// 它们帮助我们理解 C++ 如何管理对象的生命周期和内存使用
// 左值:
// 左值是在表达式结束后仍然存在的对象,具有持久性,通常可以被赋值
// 其特点如下:
// 1. 有明确的存储位置,可以通过取地址运算符 & 获取地址
// 2. 可以出现在赋值运算符 = 的左边,因为它代表的是可修改的对象
// 示例:
int x = 5; // x 是左值,因为它有存储位置,可以被修改
// 输出左值 x 的地址,证明它有存储位置
std::cout << "x 的地址: " << &x << std::endl;
x = 10; // 可以给左值 x 赋值,因为它是持久存在的
int* p = &x; // 可以获取左值 x 的地址并存储在指针 p 中
// 右值:
// 右值是临时对象或不具持久性的值,一般在表达式计算完成后即被释放
// 其特点如下:
// 1. 通常没有明确的存储位置,不能直接使用取地址运算符 & 获取地址
// 2. 右值表达式一般只能出现在赋值运算符 = 的右边,因为它是临时的
// 3. 右值通常是临时计算的结果,或者没有名称的值,比如字面值、临时对象等
// 示例:
int y = 5 + 10; // 这里 5 + 10 的结果是一个右值,它是临时的,计算完成后就不再保留
// 以下代码会导致编译错误,因为无法获取右值的地址
// int* p2 = &(5 + 10);
// 简单记忆左右值的区别:
// 左值就是有名称的、持久存储在内存中的对象,可以被持续引用和修改
// 右值就是在计算过程中产生的临时值,用完即弃,不具有持久性
#pragma endregion
#pragma region 知识点二 左值引用
// 我们之前学习的引用(使用 & 符号声明)是左值引用
// 左值引用只能绑定到左值,即绑定到持久存储在内存中的变量或对象
int a = 10;
int& refA = a; // 这是一个左值引用,refA 是 a 的别名
// 左值引用不能绑定到右值,以下代码会导致编译错误:
// int& refB = 15; // 15 是右值,不能被左值引用绑定
// int& refC = getInt(); // getInt() 的返回值是右值,不能被左值引用绑定
// 因为左值引用需要一个持久的存储位置,而右值是临时的
#pragma endregion
#pragma region 知识点三 右值引用
// 右值引用使用 && 符号声明
// 右值引用只能绑定到右值,如函数返回值、字面量等临时对象
// 特点:
// 1. 可以绑定到其他右值
// 2. 右值引用延长了右值的生命周期,使其在引用的作用域内持续存在
// 注意:
// 右值引用的意义在当前阶段可能不太明显,但在后续学习中会变得重要,例如在移动语义和完美转发中
int&& refB = 15; // 这是一个右值引用,refB 绑定到右值 15
std::cout << "refB 绑定到 15 的值: " << refB << std::endl; // 输出右值引用 refB 的值
refB = getInt(); // 可以将右值引用绑定到另一个右值(getInt() 的返回值)
std::cout << "refB 绑定到 getInt() 的值: " << refB << std::endl; // 输出更新后的 refB 的值
#pragma endregion
}
// 函数定义,返回一个整数
int getInt()
{
int i = 666;
return i;
}
34.3 练习题
左值和右值的区别是什么?
左值:指一个右持久内存地址的对象,可以出现在赋值语句的左边
右值:指一个没有持久内存地址的临时值,通常是字面量、临时对象或表达式的结果
区别是:
- 左值有持久地址,可以取得地址;右值通常没有持久地址,不能取地址
- 左值可以出现在赋值符号的左右两边;右值只能出现在右边(大部分情况下)
- 左值是持久的;右值往往是临时的
左值引用和右值引用的区别
左值引用 和 右值引用 是两种不同类型的引用,它们分别用于绑定左值和右值。
左值引用声明方式为:变量类型& 左值引用名 = 左值变量;
右值引用声明方式为:变量类型&& 右值引用名 = 右值;
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com