C++右值引用

一、右值引用的定义

   1、什么是左值和左值引用?

        左值指的是可以出现在等号左边可以被赋值(非const),可以取地址的值

        左值引用就是左值的引用,给左值取别名。(int& lr = a)

   2、什么是右值和右值引用?

        右值指的是不能出现在等号左边不能被赋值不能取地址的值

        右值引用就是右值的引用,给右值取别名。(int&& rr = 10)

   3、区别

        ①、不能以 可不可以修改,判断左值和右值。

                因为左值 const 也不能修改。

        ②、不能以 在等号左右,判断左值和右值。

                因为左值可以出现在等号右边。(但右值不能出现在等号左边)

        ③、可以用 是否能取地址,判断左值和右值。

                因为左值可以取地址,但右值不行。

        ④、引用的本质是为了减少拷贝!

   4、示例 

// 返回左值引用
int& func1()
{int* a = new int;return *a;
}// 返回右值,因为有临时拷贝
int func2()
{int a = 10;return a;
}int main()
{int a = 0; // 左值int b = 2; // 左值int& ra = a; // 左值引用/*常见的右值*/// 10// a + b// func2()/*右值引用*/int&& rra = 10;int&& rrs = a + b;return 0;
}

   5、总结

        1、语法上,引用都是取别名,不开空间,左值引用给左值取别名。右值引用就是给右值取别名。

        2、底层,引用都是用指针实现的。左值引用存的是当前左值的地址。右值引用是把右值拷贝到栈上的一个临时空间,再存放这个临时空间的地址。

        3、非const 左值引用不能给右值取别名,但const 左值引用可以。

        4、 右值引用不能给左值取别名,但是左值 move 后,可以。

二、右值引用的作用

   1、左值引用解决的问题

        前提环境:VS2019。(VS2022编译器优化太强了,无法观察)

        ①、左值引用解决了传参效率的问题,减少了拷贝。

// 非引用传参,要进行拷贝
void func(string str)
{}// 引用传参,减少了拷贝
void func(const string& str)
{}

        ②、左值引用解决了全局对象返回的拷贝问题 (未解决局部对象返回的问题)

// 返回左值引用
int& func1()
{int* a = new int;return *a;
}

   2、右值引用解决的问题

        ①、解决局部对象返回的问题 

// Test 类就是包含了一个 char* 变量做的测试
class Test
{
public:Test(const char* str = nullptr){cout << "Test(const char* str = nullptr) -- 构造\n";// 将 str 拷贝进 _strif (str){int size = strlen(str);_str = new char[size + 1];int i = 0;for (i = 0; i < size; ++i){_str[i] = str[i];}_str[i] = '\0';}}void Swap(Test& t){swap(t._str, _str);}Test(const Test& t){cout << "左值 深拷贝 -- Test(const Test& t) -- 拷贝构造\n";// 将 str 拷贝进 _strif (t._str){cout << "深拷贝\n";int size = strlen(t._str);_str = new char[size + 1];int i = 0;for (i = 0; i < size; ++i){_str[i] = t._str[i];}_str[i] = '\0';}}Test& operator=(const Test& t){cout << "左值 深拷贝 -- Test& operator=(const Test& t)\n";// 将 str 拷贝进 _strif (t._str){cout << "深拷贝\n";int size = strlen(t._str);delete _str;_str = new char[size + 1];int i = 0;for (i = 0; i < size; ++i){_str[i] = t._str[i];}_str[i] = '\0';}return *this;}/*Test(Test&& t){cout << "右值 移动拷贝 -- Test(Test&& t) -- 拷贝构造\n";Swap(t);}*/// t1 = t2;/*Test& operator=(Test&& t){cout << "右值 移动赋值 -- Test& operator=(Test&& t)\n";Swap(t);return *this;}*/~Test(){}char* _str = nullptr;
};Test func1()
{const char* str = "abc";Test t(str);return t;
}int main()
{Test t = func1();return 0;
}

        我们可以将要释放的右值称作将亡值,反正它都要被释放了,我们就可以通过右值引用(移动拷贝)将它的资源拿出来,就可以通过较低的代价,完成拷贝。 

  有人可能会有疑问:右值不能修改,为什么移动构造 Test(Test&& t) 中的 t 可以修改?

       这是因为 (四.2) 右值被右值引用后,右值引用变量的属性是左值,因此,t 可以被修改.

        上述代码,在类 Test 中,有需要深拷贝的成员 char* _str;

        如果没有移动构造,就需要先构造,再拷贝构造

        而有了移动构造,就可以先构造,再移动构造

        拷贝构造需要深拷贝,而移动构造只需要交换资源即可! 

        因为左值不能掠夺资源,而右值可以。

        注释掉移动构造:

        有移动构造:


        拷贝赋值:

Test& operator=(const Test& t){cout << "左值 深拷贝 -- Test& operator=(const Test& t)\n";if (t._str){cout << "深拷贝\n";int size = strlen(t._str);delete _str;_str = new char[size + 1];int i = 0;for (i = 0; i < size; ++i){_str[i] = t._str[i];}_str[i] = '\0';}return *this;}
Test func1()
{const char* str = "abc";Test t(str);return t;
}int main()
{Test t;t = func1();return 0;
}

         在没有移动拷贝和移动赋值的情况下:

                要进行两次深拷贝。因为分两行写,编译器无法识别,优化。

         移动赋值: 

Test& operator=(Test&& t)
{cout << "右值 移动赋值 -- Test& operator=(Test&& t)\n";Swap(t);return *this;
}

         在有移动拷贝和移动赋值的情况下:

                不用进行深拷贝。效率大大提高!

 三、右值概念的细分

        1、纯右值(内置类型的右值)

                如:a + b、10

        2、将亡值(自定义类型的右值)

                如:匿名对象、传值返回函数 (如上例)

        C++ 提供右值引用,就是为了能够区分左值和右值,左值的资源不能乱动,但将亡值我们可以将它的资源进行利用。

        3、总结

        浅拷贝的类不需要移动构造(右值引用)

        只有要深拷贝的类才需要移动构造(右值引用)。 

四、有关右值引用的相关知识

        1、move 函数 

       move(leftval)函数的作用是将左值参数变为右值返回,move 以后左值还是左值,只是 move 的返回值是右值。注意:如果 move 后的左值的资源被掠夺,那么该左值的资源也会消失。  

int main()
{// Test 类就是包含了一个 char* 变量做的测试// 有关 Test 类的具体定义在上面哦Test t1("aaaa");Test t2(move(t1));return 0;
}


         2、右值被右值引用以后,右值引用变量的属性是左值。 

Test func1()
{const char* str = "abc";Test t(str);return t;
}int main()
{Test&& r = func1(); // r 是左值Test t(r);return 0;
}

        我们可以看到,上述代码中,如果 r 是右值,那么 main() 函数内的 t 应该调移动构造,但它实际上调的是拷贝构造,因此得出结论,r 是左值。


        3、万能引用&完美转发

        在模版中,为了简化工作,让我们可以不写两份函数区分左值和右值,参数部分直接写为 T&&,模版会自动推演这是左值还是右值 (左值、右值、const 左值、const 右值 都可以匹配),这被称为万能引用。

        它也被称作引用折叠,左值就推演为 (T&& -> T&),右值就不变 (T&& -> T&&)。

template<class T>
void PerfectForward(T&& t)
{// 传左值,t就是左值引用// 传右值,t就是右值引用
}

        我们知道,t 是左值,如果在 PerfectForward 函数内部还想区分左值和右值怎么办呢?

        那就需要完美转发。 

        完美转发:forward<T>(t) 在传参过程中保持了 t 的原生类型属性。 

void func(int& x)
{cout << "func(int& x)" << endl;
}void func(const int& x)
{cout << "func(const int& x)" << endl;
}void func(int&& x)
{cout << "func(int&& x)" << endl;
}void func(const int&& x)
{cout << "func(const int&& x)" << endl;
}template<class T>
void PerfectForward(T&& t)
{// 传左值,t就是左值引用// 传右值,t就是右值引用// func(t);  --> 未使用完美转发func(forward<T>(t)); // ---> 完美转发
}int main()
{PerfectForward(10); // 右值int a = 10;int& ra = a;PerfectForward(ra); // 左值引用const int& cra = a;PerfectForward(cra);// const 左值引用return 0;
}

        未使用完美转发,全是左值引用。

        使用完美转发:保持了原生类型属性

         感谢观看♪(・ω・)ノ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/462556.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】时间序列预测、分类、异常检测、概率预测项目实战案例

说明&#xff1a;本专栏内容来自于个人学习笔记、以及相关项目的实践与总结。写作目的是为了让读者体会深度学习的独特魅力与无限潜力&#xff0c;以及在各行各业之中的应用与实践。因作者时间精力有限&#xff0c;难免有疏漏之处&#xff0c;期待与读者共同进步。 前言 在当今…

【重生之我要苦学C语言】深入理解指针2

深入理解指针2 const修饰指针 当const修饰变量时&#xff0c;是无法更该该变量的值的 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() {const int a 10;//const常属性&#xff0c;不能改变的属性a 1;printf("%d\n", a);return 0; }报错&…

WPF+MVVM案例实战(十八)- 自定义字体图标按钮的封装与实现(ABD类)

文章目录 1、案例效果1、按钮分类2、ABD类按钮实现描述1.文件创建与代码实现2、样式引用与控件封装3、按钮案例演示1、页面实现与文件创建2、运行效果如下3、总结4、源代码获取1、案例效果 1、按钮分类 在WPF开发中,最常见的就是按钮的使用,这里我们总结以下大概的按钮种类,…

ARM base instruction -- mneg

Multiply-Negate multiplies two register values, negates the product, and writes the result to the destination register. 乘法-求反&#xff0c;将两个寄存器值相乘&#xff0c;对乘积求反&#xff0c;并将结果写入目标寄存器。 32-bit variant Applies when sf 0…

【鸿蒙新闻】10月29日警用鸿蒙开发者大会在北京胜利召开,开启智慧应用新时代!

10月29日&#xff0c;在公安部科技信息化局、公安部装备财务局指导下&#xff0c;由公安部第一研究所主办&#xff0c;鼎桥通信技术有限公司、OpenHarmony生态委员会及公共安全专委会协办的警用鸿蒙开发者大会在北京胜利召开。会议以“拥抱警鸿创新生态 开启智慧应用新时代”为…

架构师备考-软件工程相关补充

软件开发生命周期 按照传统的软件生命周期方法学&#xff0c;可以把软件生命周期划分为软件定义、软件开发、软件运行与维护三个阶段。 软件定义&#xff1a;软件定义包括可行性研究和详细需求分析过程&#xff0c;任务是确定软件开发工程必须完成的目标。具体可分为问题定义、…

OpenGL入门003——使用Factory设计模式简化渲染流程

前面两节已经学会了如何使用opengl创建窗口并绘制三角形&#xff0c;我们可以看出有些步骤是固定的&#xff0c;而且都写在main.cpp&#xff0c;这一节我们将了解如何使用Factroy设计模型。将模型渲染逻辑封装在一个单独的类中&#xff0c;简化开发流程&#xff0c;且提高代码复…

音频中sample rate是什么意思?

‌sample rate‌在数字信号处理中&#xff0c;指的是‌采样频率‌&#xff0c;即每秒钟从连续信号中抽取的样本数量。采样频率越高&#xff0c;信号的还原度越高&#xff0c;但同时也会增加计算负担和存储需求‌。 实际应用场景 在音频处理中&#xff0c;设置合适的采样率可以…

分享一下面试中常用的10 个面试点全解析,面试成功的秘诀

大家好&#xff0c;我是一颗甜苞谷&#xff0c;今天分享一下面试中常用的10 个面试点全解析,助你面试中脱颖而出 问题1&#xff1a;微服务架构和传统架构有什么区别&#xff0c;现在市场上的微服务架构有哪些? 答&#xff1a;传统的单体架构可维护性、可读性低&#xff0c;维…

构建品牌影响力:知识库工具在市场营销中的创新应用

在当今这个信息爆炸的时代&#xff0c;品牌影响力成为了企业市场竞争力的核心要素。为了有效提升品牌影响力&#xff0c;企业不仅需要精准的市场定位和优质的产品服务&#xff0c;还需要借助高效、智能的知识库工具来优化其市场营销策略。本文将探讨知识库工具在市场营销中的创…

Python Matplotlib 子图绘制

Python 中的子图绘制 在数据可视化中&#xff0c;展示多个图表在同一个画布上是常见的需求&#xff0c;这样可以更直观地比较不同数据集之间的关系。Python 中的 Matplotlib 库为我们提供了强大的功能来实现这一点。在本篇文章中&#xff0c;我们将详细介绍如何使用 Matplotli…

探索设计模式:命令模式

探索设计模式&#xff1a;命令模式 &#x1f9d0;1. 概念&#x1f3af;2. 作用&#x1f4e6;3. 实现3.1 定义命令接口3.2 实现具体命令3.3 实现接收者3.4 实现调用者3.5 使用 &#x1f4bb;4. 应用场景 命令模式&#xff08;Command Pattern&#xff09;就是一种行为型设计模式…

Python-创建并调用自定义文件中的模块/函数

背景&#xff1a;在Python编程中&#xff0c;我们常常需要创建自己的专属文件&#xff0c;以便帮助我们更高效&#xff0c;快捷地完成任务。那么在Python中我们怎么创建并调用自己文件中的模块/函数呢? 在Python中调用自定义文件&#xff0c;通常是指调用自己编写的Python模块…

springboot 修复 Spring Framework 特定条件下目录遍历漏洞(CVE-2024-38819)

刚解决Spring Framework 特定条件下目录遍历漏洞&#xff08;CVE-2024-38816&#xff09;没几天&#xff0c;又来一个新的&#xff0c;真是哭笑不得啊。 springboot 修复 Spring Framework 特定条件下目录遍历漏洞&#xff08;CVE-2024-38816&#xff09;https://blog.csdn.ne…

AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion论文阅读笔记

AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion 论文阅读笔记 这是ECCV2024的论文&#xff0c;作者单位是是港中文和上海AI Lab 文章提出了一个叫AutoDIR的方法&#xff0c;包括两个关键阶段&#xff0c;一个是BIQA&#xff0c;基于vision-language…

CDN加速实战:使用七牛云CDN加速阿里云OSS资源访问

今天是双11搞活动,在阿里云1元注册了个域名,想着在学CDN,想使用CDN做个加速项目,但是阿里的要收费,上网查了下七牛云的不收费,想着将七牛云的CDN结合阿里的DNS做个访问加速,刚好看到了阿里的一个文章,照着改了改,实践成功了。 阿里文章:使用CDN加速OSS资源访问_对象…

MacBook 如何设置打开json格式文件的默认程序是vs code

首先右键选中文件&#xff0c;然后选中显示简介 然后选中打开方式 设置成vs code

HTML 基础标签——文本内容标签 <ul>、<ol>、<blockquote> 、<code> 等标签的用法详解

文章目录 1. 标题标签2. 段落标签3. 文本格式化标签4. 列表标签4.1 无序列表 `<ul>`4.2 有序列表 `<ol>`5. 引用标签5.1 块引用 `<blockquote>`5.2 行内引用 `<q>`5.3 作品引用 `<cite>`6. 代码和预格式文本标签6.1 代码标签 `<code>`6.2 …

git 删除远程不存在本地命令却能看到的分支

要删除远程不存在但本地却能看到的分支&#xff0c;你可以按照以下步骤操作&#xff1a; 删除本地分支&#xff1a; 如果你确定要删除的分支已经没有用处&#xff0c;可以使用以下命令来删除本地分支&#xff1a; git branch -d <branch-name>这里的 <branch-name>…

【Oracle APEX开发小技巧10】CSS样式控制交互式报表列宽和自动换行效果

在实际开发中使用交互式报表可能会出现某些字段的列宽过长&#xff0c;某些字段的列宽只有缩到一角的情况&#xff0c;那么如何解决这种情况呢&#xff1f;有没有方法可以控制交互式报表的列宽呢&#xff1f;下面就来介绍一下解决方法&#xff1a; 页设置-页-CSS-内嵌 输入如下…