
离线 TA的专栏
- 打卡等级:无名新人
- 打卡总天数:1
- 打卡月天数:0
- 打卡总奖励:14
- 最近打卡:2025-03-10 00:37:20
|
左值 (Lvalue)
定义:
- 表达式结束后依然存在的持久对象。
- 有名字、有持久性的表达式,它是既能够出现在等号左边,也能出现在等号右边的变量。
右值 (Rvalue)
定义:
- 表达式结束后就不再存在的临时对象。
- 临时的、将亡的值。一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象,短暂性的。
左值和右值主要的区别之一是左值可以被修改,而右值不能。 在这段代码中 number 为左值 ,1 为右值。 - // getValue()返回的临时值是右值
- int value = getValue();
复制代码引用的本质是别名,可以通过引用改变变量的值,传参时传引用可以避免拷贝。
左值引用 (Lvalue Reference)
定义: 引用一个对象;
基本用法: - int number = 10;
- int& count = number;
- count++; // number = count = 11;
复制代码
- number 是左值,10 是右值
- count 是左值引用,count 引用 number
- void addCount(int& count)
- {
- count++;
- }
- int number = 10;
- addCount(number);
复制代码如果给“左值”引用直接赋“右值”,则会报错,例: - int& number = 10;
- 报错:
- 错误 C2440 “初始化”: 无法从“int”转换为“int &”
复制代码 const 左值引用不会修改指向值,因此可以指向右值
例如: - std::vector 中的方法 push_back
- // 如果没有 const 那么使用 v.push_back(10) 这样 10 的类型是右值,就无法通过编译了
- void push_back(const value_type& _Val)
- // 可以直接传递字面值
- v.push_back(10)
- // 可以传递表达式结果
- v.push_back(x+y)
复制代码 右值引用 (Rvalue Reference)
定义:就是必须绑定到右值的引用,它可以指向右值,不能指向左值。C++11中右值引用可以实现“移动语义”,通过 && 获得右值引用。
右值引用绑定右值 错误:右值引用不能直接绑定左值 复制代码 尝试让右值引用指向左值 - int count = 2;
- // 使用 std::move 把左值强制转换为右值 ,number = 2
- int&& number1 = std::move(count);
- // 等同与 std::move 类型转换 ,number = 2
- int&& number2 = static_cast<int&&>(count);
- // count = number1 = number2 = 10
- number1 = 10;
复制代码简单练习: - void test(int & o) {std::cout << "左值。" << std::endl;}
- void test(int && temp) {std::cout << "右值。" << std::endl;}
- int main(){
- int a;
- int && b = 10;
- test(a);
- test(std::move(a));
- test(b);
- }
- 结果:
- a 是一个具名变量,有固定的内存地址是典型的左值。输出:"左值。"
- std::move(a) 将左值 a 转换为右值引用 返回类型是 int&&。输出:"右值"
- 虽然 b 的类型是右值引用(int&&)但 b 本身是一个具名变量,可以取地址。 输出:"左值"
复制代码结论:右值引用类型只是用于匹配右值,而并非表示一个右值。因此,尽量不要声明右值引用类型的变量,而只在函数形参使用它以匹配右值。
浅拷贝(Shallow Copy)
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存(分支)。
- 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
- 如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝(deep copy)
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是“值”而不是“引用”(不是分支)
- 拷贝第一层级的对象属性或数组元素
- 递归拷贝所有层级的对象属性和数组元素
- 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
编写代码: - #include <iostream>
- class Vector {
- int num;
- int* a;
- public:
- // 构造函数
- Vector(int n = 0) : num(n) {
- a = new int[num];
- for (int i = 0; i < num; ++i) {
- a[i] = i + 1; // 初始化为1,2,3...
- }
- }
- // 析构函数
- ~Vector() {
- delete[] a;
- }
- void ShallowCopy(Vector& v);
- void DeepCopy(Vector& v);
- // 打印数组内容的辅助函数
- void print() const {
- std::cout << "num = " << num << ", array content: ";
- for (int i = 0; i < num; ++i) {
- std::cout << a[i] << " ";
- }
- std::cout << std::endl;
- }
- // 修改数组内容的辅助函数
- void modify(int index, int value) {
- if (index >= 0 && index < num) {
- a[index] = value;
- }
- }
- };
- // 浅拷贝实现
- void Vector::ShallowCopy(Vector& v) {
- this->num = v.num;
- this->a = v.a;
- }
- // 深拷贝实现
- void Vector::DeepCopy(Vector& v) {
- delete[] this->a; // 先释放原有内存
- this->num = v.num;
- this->a = new int[num];
- for (int i = 0; i < num; ++i) {
- a[i] = v.a[i];
- }
- }
复制代码 浅拷贝测试
- int main() {
- // 测试浅拷贝
- std::cout << "=== 测试浅拷贝 ===" << std::endl;
- {
- Vector v1(5); // 创建一个包含5个元素的向量
- std::cout << "Original v1: ";
- v1.print();
- Vector v2(3); // 创建另一个向量
- std::cout << "Original v2: ";
- v2.print();
- v2.ShallowCopy(v1); // 浅拷贝
- std::cout << "After shallow copy, v2: ";
- v2.print();
- // 修改v1,观察v2是否变化
- v1.modify(0, 100);
- std::cout << "After modifying v1, v1: ";
- v1.print();
- std::cout << "After modifying v1, v2: ";
- v2.print();
- // 这里会崩溃,因为v1和v2都试图删除同一块内存
- std::cout << "Program will crash here due to double delete\n";
- }
- }
- 程序输出:
- === 测试浅拷贝 ===
- Original v1: num = 5, array content: 1 2 3 4 5
- Original v2: num = 3, array content: 1 2 3
- After shallow copy, v2: num = 5, array content: 1 2 3 4 5
- After modifying v1, v1: num = 5, array content: 100 2 3 4 5
- After modifying v1, v2: num = 5, array content: 100 2 3 4 5
- Program will crash here due to double delete
- 崩溃位置:
- // 析构函数
- ~Vector() {
- delete[] a;
- }
复制代码 深拷贝测试
- int main() {
- // 测试深拷贝
- std::cout << "\n=== 测试深拷贝 ===" << std::endl;
- {
- Vector v1(5);
- std::cout << "Original v1: ";
- v1.print();
- Vector v2(3);
- std::cout << "Original v2: ";
- v2.print();
- v2.DeepCopy(v1); // 深拷贝
- std::cout << "After deep copy, v2: ";
- v2.print();
- // 修改v1,观察v2是否变化
- v1.modify(0, 100);
- std::cout << "After modifying v1, v1: ";
- v1.print();
- std::cout << "After modifying v1, v2: ";
- v2.print();
- // 这里不会崩溃,因为每个对象管理自己的内存
- std::cout << "Program will exit normally\n";
- }
- return 0;
- }
- === 测试深拷贝 ===
- Original v1: num = 5, array content: 1 2 3 4 5
- Original v2: num = 3, array content: 1 2 3
- After deep copy, v2: num = 5, array content: 1 2 3 4 5
- After modifying v1, v1: num = 5, array content: 100 2 3 4 5
- After modifying v1, v2: num = 5, array content: 1 2 3 4 5
- Program will exit normally
复制代码 再探右值引用
使用 C++ 11 的右值引用,重载拷贝函数,代码修改为: - class Vector {
- int num;
- int* a;
- public:
- // 构造函数
- Vector(int n = 0) : num(n) {
- a = new int[num];
- for (int i = 0; i < num; ++i) {
- a[i] = i + 1; // 初始化为1,2,3...
- }
- }
- // 析构函数
- ~Vector() {
- delete[] a;
- }
-
- //左值引用形参=>匹配左值
- void Copy(Vector& tmp) {
- if (nullptr != this->a){
- delete[] this->a;
- }
-
- this->num = tmp.num;
- this->a = tmp.a;
-
- // 防止tmp析构时删除内存
- tmp.a = nullptr;
- }
-
- //右值引用形参=>匹配右值
- void Copy(Vector&& tmp) {
- this->num = tmp.num;
- this->a = tmp.a;
- }
- };
复制代码 调用测试
- int main() {
- // 测试左值引用版本
- Vector v1(5); // v1: {1,2,3,4,5}
- Vector v2;
- v2.Copy(v1); // 深拷贝:v2获得自己的内存副本
- // 测试右值引用版本
- Vector v3;
- v3.Copy(Vector(3)); // 移动:直接窃取临时对象的资源
- return 0;
- }
复制代码 总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:  进行删除处理。
4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
|