当前位置: 首页 > news >正文

C++中的右值引用

对右值引用的用法介绍

前言

C++98中提出了引用的概念,引用即别名,引用变量与其引用实体公共同一块内存空间,而引用的底层是通过指针来实现的,因此使用引用,可以提高程序的可读性。

右值引用的最大作用:我们要转移数据时需要进行拷贝,太消耗资源,代价很大,有时候这些对象是临时对象就是所说的“将亡值”,这些临时对象马上就要销毁了,我们能不能不复制,直接把资源拿过来,下面介绍做法。


一、右值引用的概念

为了提高程序运行效率,C++11中引入了右值引用,右值引用也是别名,但其只能对右值引用。

这里区别一下左值和右值的不同;左值可以进行修改,左值通常是变量,比如

int x=10; x=11;

x就是一个左值,可以进行改变,
但是对于右值,通常是常量,比如10就是一个常量,右值也可以是表达式或者函数返回值(临时对象),

右值引用 int&& a=10;

左值与右值是C语言中的概念,但C标准并没有给出严格的区分方式,一般认为:可以放在=左边的,或者能够取地址的称为左值,只能放在=右边的,或者不能取地址的称为右值,但是也不一定完全正确。
因此关于左值与右值的区分不是很好区分,一般认为:
1.普通类型的变量,因为有名字,可以取地址,都认为是左值。
2. const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间),C++11认为其是左值。
3.如果表达式的运行结果是一个临时变量或者对象,认为是右值。
4.如果表达式运行结果或单个变量是一个引用则认为是左值。

总结:
1.不能简单地通过能否放在=左侧右侧或者取地址来判断左值或者右值,要根据表达式结果或变量的性质判断,比如上述:c常量
2.能得到引用的表达式一定能够作为引用,否则就用常引用。
C++11对右值进行了严格的区分:C语言中的纯右值,比如:a+b, 100
将亡值。比如:表达式的中间结果、函数按照值的方式进行返回。

注:左值引用不能引用右值,但是const左值引用可以,
普通引用只能引用左值,不能引用右值,const引用既可引用左值,也可引用右值。
C++11中右值引用:只能引用右值,一般情况不能直接引用左值。

问题:既然C++98中的const类型引用左值和右值都可以引用,那为什么C++11还要复杂的提出右值引用呢?

二、值的形式返回对象的缺陷

如果一个类中涉及到资源管理,用户必须显式提供拷贝构造、赋值运算符重载以及析构函数,否则编译器将会自动生成一个默认的,如果遇到拷贝对象或者对象之间相互赋值,就会出错。

比如在对一个数进行函数调用时,按照值返回,必须床造一个临时对象,临时对象进行返回,又创建一个临时空间,然后进行构造,好了之后又进行销毁临时对象,这样创建又销毁,对空间就是一种浪费,且会造成程序的效率降低。

所以我们引出移动语义,将资源直接转移到另一个对象中。

三、移动语义

C++11提出了移动语义概念,即:将一个对象中资源移动到另一个对象中的方式,可以有效缓解该问题。

String (const String& s) { cout<<"String(const String&s)-深拷贝-效率低"<<endl; _str=new char[strlen(s._str)+1]; strcpy(_str,s._str); }

这是一个深拷贝,需要创建一个临时对象,然后进行拷贝传值,会影响效率;且对空间有消耗。

String(String&&s) :_str(nullptr) { cout<<"String(String&&s)-移动拷贝-效率高"<<endl; swap(_str,s._str); }

这是右值引用,也就是直接将要销毁的值直接交换给新的空间,不用麻烦创建临时对象进行拷贝了;同时避免了拷贝构造整个过程,只需要创建一块堆内存即可,既省了空间,又大大提高程序运行的效率。

注:1.移动构造函数的参数千万不能设置成const类型的右值引用,因为资源无法转移而导致移动语义失效。
2.在C++11中,编译器会为类默认生成一个移动构造,该移动构造为浅拷贝,因此当类中涉及到资源管理时,用户必须显式定义自己的移动构造。

显式构造一个移动构造:

String(String&& s) : _str(s._str) { s._str = nullptr; }

移动构造的一个例子

String s3= s1+=s2;//拷贝构造 String s4=s1+s2;//移动构造

现实中不可避免存在传值返回的场景,传值返回拷贝返回对象的临时对象,
若vector只实现参数为const左值引用深拷贝,那么下面的代价就很大。
vector(const vector<T>& v)->深拷贝

但是若使用了右值引用的移动拷贝,那么这里的效率就会很高;
vector(vector<T> && v)->移动拷贝
注:右值引用本身没有多大的意义,只是右值引用实现的移动拷贝和移动赋值有用
接收函数和传值返回对象的场景,可以提高效率;

四、右值引用左值

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?因为:有些场景下,可能真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中,std::move()函数位于头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。

template<class _Ty> inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT { // forward _Arg as movable return ((typename remove_reference<_Ty>::type&&)_Arg); }

注意:
1.被转化的左值,其生命周期并没有随着左值的转化而改变,即std::move转化的左值变量lvalue不会被销毁。
2. STL中也有另一个move函数,就是将一个范围中的元素搬移到另一个位置。

int main() { String s1("hello world"); String s2(move(s1)); String s3(s2); return 0; }

注意:以上代码是move函数的经典的误用,因为move将s1转化为右值后,在实现s2的拷贝时就会使用移动构造,此时s1的资源就被转移到s2中,s1就成为了无效的字符串。

学习一下emplace_back
这是push_back的插入

vector<string,string> v; v.push_back(make_pair("左值","右值"));

而emplace_back模版可变参数的特点

v.emplace_back("右值","左值");

五、完美转发

完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数

所谓完美:函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果=应实参是右值,它就应该被转发为右值。这样做是为了保留在其他函数针对转发而来的参数的左右值属性进行不同处理(比如参数为左值时实施拷贝语义;参数为右值时实施移动义)。

C++11通过forward函数来实现完美转发

void Fun(int &x){cout << "lvalue ref" << endl;} void Fun(int &&x){cout << "rvalue ref" << endl;} void Fun(const int &x){cout << "const lvalue ref" << endl;} void Fun(const int &&x){cout << "const rvalue ref" << endl;} template<typename T> void PerfectForward(T &&t){Fun(std::forward<T>(t));} int main() { PerfectForward(10); // rvalue ref int a; PerfectForward(a); // lvalue ref PerfectForward(std::move(a)); // rvalue ref const int b = 8; PerfectForward(b); // const lvalue ref PerfectForward(std::move(b)); // const rvalue ref return 0; }

PerfectForward为转发的模板函数,Fun为实际目标函数,完美转发是目标函数总希望将参数按照传递给转发函数的实际类型转给目标函数,而不产生额外的开销,就好像转发者不存在一样。

所以使用forward可以解决这个问题。

六、右值引用的作用

C++11中右值引用主要有以下作用:
1.实现移动语义(移动构造与移动赋值)
2.给中间临时变量取别名:
3.实现完美转发

int main() { string s1("hello"); string s2(" world"); string s3 = s1 + s2;// s3是用s1和s2拼接完成之后的结果拷贝构造的新对象 stirng&& s4 = s1 + s2; // s4就是s1和s2拼接完成之后结果的别名 return 0; }

总结

对右值引用进行介绍,右值引用最大的作用在于移动构造,减少空间的消耗,提高效率,。

在开发中的作用是使用右值引用,进行对临时对象或者即将消耗的对象(将亡值)进行转移,而不是拷贝构造。

http://www.jsqmd.com/news/797279/

相关文章:

  • FanControl深度解析:5个步骤让Windows风扇控制从入门到精通
  • 2026广州房产自媒体博主推荐必看! - 速递信息
  • 2026年5月更新:欧米茄腕表常见故障与全国预约服务中心指引 - 博客万
  • Nigate:Mac用户的NTFS读写救星,3分钟解决跨平台文件传输难题
  • 汽车零部件缺陷检测:YOLOv8从1.2FPS到35FPS的全链路优化实战
  • 2026届学术党必备的十大降AI率神器推荐
  • 告别乱码!手把手教你用LvglFontTool v0.4为LVGL 8.x生成精简中文字库
  • 从SkewT到传统T-LnP:用Python metpy定制气象专业图
  • 【HDLC 高级数据链路控制协议】
  • 94.二叉树的中序遍历
  • 2026年江苏电动破碎阀与北京水泥块料破碎机行业深度横评:五大品牌对标与智能防堵解决方案选购指南 - 年度推荐企业名录
  • DISTINCT 明明有 WHERE 条件,为什么还要全表扫描?KES数据库去重优化的两层刀法
  • 2026年江苏电动破碎阀与工业防堵系统深度横评:从水泥厂到化工厂的智能化解决方案 - 年度推荐企业名录
  • 崩坏星穹铁道终极自动化指南:三月七小助手帮你每天节省2小时游戏时间
  • Windows掌机游戏体验终极优化指南:HandheldCompanion完全教程
  • 英雄联盟Akari助手:智能游戏伴侣让你的排位赛效率提升10倍
  • LaTeX2Word-Equation:如何3分钟完成网页公式到Word的完美迁移
  • 5G信号不好时,基站是怎么偷偷帮你“降速保命”的?聊聊AMC与MCS的实战逻辑
  • 如何快速检测微信单向好友:WechatRealFriends终极解决方案
  • G-Helper终极指南:3分钟掌握华硕笔记本性能优化,告别Armoury Crate臃肿体验
  • 江苏省本地CPPM官方授权报名中心及联系方式 - 众智商学院课程中心
  • 3月20日起,Ledger中文官网正式上线!你手里的硬件设备,是正品还是假货?一键就能查 - 博客万
  • 【运算篇】算术与逻辑律令(2):地牢里的感官,逻辑指令的“瞬间审判“
  • 用Wireshark和Python脚本‘解剖’USB协议:一步步解析Device Qualifier Descriptor抓包数据
  • 从信息学奥赛真题到项目实战:C++浮点数精度那些坑,你的double真的够用吗?
  • XCursor主题编译工具链:从SVG到Linux光标主题的自动化实践
  • 如何轻松解锁加密音乐:Unlock-Music 免费工具终极指南 [特殊字符]
  • PCDViewer-2.0:从数据加载到深度洞察,解锁点云可视化新维度
  • 2026年江苏电动破碎阀与水泥块料破碎机深度选购指南|凯德斯官方对接 - 年度推荐企业名录
  • 深度解析 TailGrids 3.0:现代化 React UI 库的重构之道