详解C++中右值引用

98中的引用

  1. 概念
  2. 特性
  3. 引用的使用场景
  4. 三种传参方式效率的比较
  5. 探索:引用的底层实现方式----->指针
    • T&------>T* const
    • const T&---->const T*const
  6. 引用和指针的区别
引用的总结

11中的右值引用

为什么要有右值引用

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

int main()
{//右值引用---->引用形式----->只能引用右值int a = 10;int b = 20;//a可以为左值,能放到=号左侧的一定是左值//能放到=号右侧的不一定是右值a = b;//ra对10的右值引用,ra称为对10的别名int&& ra = 10;system("pause");return 0;
}

左值与右值

一般认为认为:可以放在=左边的,或者能够取地址的称为左值,只能放在=右边的,或者不能取地址的称为右值,但肯定有特殊情况,不一定上述说法就完全正确

int  main()
{const int a = 10;//a = 100; a不能出现在左侧//a也不是右值//a能够取地址cout << &a << endl;//int && ra = a;system("pause");return 0;
}

左值与右值总结

左值与右值不是很好区分,一般认为:

  1. 普通类型的变量,因为有名字,可以取地址,都认为是左值
  2. const修饰的常量,不可修改,只读类型的,理论上应该按照右值对待,但因为其可以取地址(如果只是const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间),C++11认为其是左值
  3. 如果表达式的运行结果是一个临时变量或者对象,认为是右值
  4. 如果表达式运行结果或单个变量是一个引用则认为是左值
int b = 10;//为右值
int&& rb = b + 10;
int g_a = 10;int& GetG_A()
{return g_a;
}int main()
{GetG_A() = 10;//下面这行代码编译报错//int && rc = GetG_A();return 0;
}

不能简单的通过能否放在=左侧右侧或者取地址来判断左值或者右值,要根据表达式结果或变量的性质判断,比如上述:c常量

C++11中的右值

右值引用,顾名思义就是对右值的引用。C++11中,右值由两个概念组成:纯右值和将亡值。

  • 纯右值
    纯右值是C++98中右值的概念,用于识别临时变量和一些不跟对象关联的值。比如:常量、一些运算表达式(1+3)等
  • 将亡值
    声明周期将要结束的对象。比如:在值返回时的临时对象。
//将亡值
//值得方式返回
//使用ret--->创建一个临时变量---->函数运行结束栈空间被回收
int Add(int a, int b)
{int ret = a + b;return ret;
}int main()
{int&& Ret = Add(10,20);return 0;
}

引用与右值引用的比较

//C++98引用
//1. 普通类型得引用
//2. cosnt类型的引用
int main()
{//98中的普通类型的引用不能引用右值int a = 10;int&ra = a;//int&rra = 10;	不行//98中的const引用既可以引用左值也可以引用右值const int&cral = a;const int& cra2 = 10;return 0;
}

C++11中右值引用:只能引用右值,一般情况不能直接引用左值

int main()
{//10纯右值,本来只是一个符号,没有具体空间//右值引用变量r1在定义过程中,编译器产生了一个临时变量,r1实际引用的是临时变量int&& r1=10;r1=100;int a=10;int&& r2=a; //编译失败:右值引用不能引用左值return 0;
}

std::move()

C++11中,std::move()函数位于 头文件中,这个函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,通过右值引用使用该值,实现移动语义。 注意:被转化的左值,其声明周期并没有随着左右值的转化而改变,即std::move转化的左值变量不会被销毁

int main()
{int a = 10;int&& ra = move(a);return 0;
}

右值引用的场景

class String
{
public:String(char* str = ""){if (nullptr == str)str = "";_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}String& operator=(const String& s){if (this != &s){char* pTemp = new char[strlen(s._str) + 1];strcpy(pTemp, s._str);delete[] _str;_str = pTemp;}return *this;}String operator+(const String&s){char* pTemp = new char[strlen(_str) + strlen(s._str)];strcpy(pTemp, _str);strcpy(pTemp + strlen(_str), s._str);String strRet(pTemp);delete pTemp;return strRet;}~String(){if (_str) delete[] _str;}
private:char* _str;
};
int main()
{String s1("hello");String s2("world");String s3(s1 + s2);return 0;
}

假设编译器在返回值位置不优化
上述代码在加号运算符的重载时,是创建了临时变量,存储拼接起来的字符串,返回的时候是按值返回是通过strRet拷贝了一个临时的对象。函数体内的strRet在return后,已经被销毁。所以最后s3拷贝构造的时候是通过临时对象构造的,所以s3也要再创建一个临时变量,一但拷贝构造好之后,临时对象就释放掉了
一个刚释放一个又申请,有点多此一举。能不能不让s3再创建一个临时对象
我们可以让s3直接使用返回值的临时对象。

移动语义

C++11提出移动语义概念:将一个对象中资源移动到另外一个对象的方式

  1. 可以有效的缓解内存的压力
  2. 提高程序的运行效率,因为不需要开辟空间和释放空间
//移动构造
//移动构造参数一定不能加cosnt,资源可以转移出去,但是资源不能清空了
String(String&& s):_str(s._str)
{s._str = nullptr;
}
  1. 移动构造函数参数不能加const类型的右值引用,因为资源无法转移而导致语义失效
  2. 在C++11中,编译器会为类默认生成一个移动构造,该移动构造为浅拷贝,因此类中涉及到资源管理时,用户必须显示定义自己的移动构造
    在C++11中,拷贝构造/移动构造/赋值/移动赋值函数必须同时提供,或者同时不提供,程序才能保证类同时具有拷贝和移动语义

auto_ptr中的资源转移

	auto_ptr<int> sp1(new  int);auto_ptr<int> sp2(sp1);//sp1资源给了sp2,但是sp1生命周期还没有到头,所以不能被使用

右值引用引用左值

通过move函数把左值转化为右值。

class Person
{
public:Person(char* name, char* sex, int age): _name(name), _sex(sex), _age(age){}Person(const Person& p): _name(p._name), _sex(p._sex), _age(p._age){}
#if 0Person(Person&& p): _name(p._name), _sex(p._sex), _age(p._age){}
#elsePerson(Person&& p): _name(move(p._name)), _sex(move(p._sex)), _age(p._age){}//移动赋值Person& operator=(Person&&p){return *this;}
#endif
private:String _name;String _sex;int _age;
};
Person GetTempPerson()
{Person pp("prety", "male", 18);return pp;		//pp确实是右值,但是里面的成员变量是当作左值,所以用move把左值转化为右值
}
int main()
{Person p(GetTempPerson());system("pause");return 0;
}

用move的时机,确定当前变量是将亡值,例如函数返回值

完美转发

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

void Func(int x)
{// ......
}
template<typename T>
void PerfectForward(T t)
{Fun(t);
}

PerfectForward为转发的模板函数,Func为实际目标函数,但是上述转发还不算完美**,完美转发是目标函数总希望将参数按照传递给转发函数的实际类型转给目标函数,而不产生额外的开销**,上述代码中的Fun(t)只是外部t的一份拷贝,就好像转发者不存在一样。

所谓完美:

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

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

void Fun(int &x)	//普通类型引用
{ cout << "lvalue ref" << endl; 
}
void Fun(int &&x)	//普通类型右值引用
{ cout << "rvalue ref" << endl; 
}
void Fun(const int &x)	//const类型普通引用
{cout << "const lvalue ref" << endl; 
}
void Fun(const int &&x)	//const类型的右值引用
{ cout << "const rvalue ref" << endl; 
}//通过函数模板作为转发函数
template<typename T>
//完美转发:t是左值---->传给Fun函数,t应该也是左值
//		   t如果是右值--->传给Fun函数,t应该是右值
void PerfectForward(T &&t)
{ Fun(std::forward<T>(t)); //forward把t转化为右值。//Fun(t);
}int main()
{PerfectForward(10); // 10为右值int a;PerfectForward(a); // lvalue refPerfectForward(std::move(a)); // rvalue refconst int b = 8;PerfectForward(b); // const lvalue refPerfectForward(std::move(b)); // const rvalue refreturn 0;
}

foward和move

  • move实现移动语义
  • forward实现完美转发

右值引用作用

  1. 实现移动语义
  2. 给中间临时变量取别名
  3. 实现完美转发
  4. unordered_map 中的构造和插入都有用到右值引用。vectorC++11中的emplace插入(尾插)也用到了右值引用。

pash_back;必须要把对象构造好
emplace:构造加尾插

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

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

相关文章

C++中的lambda表达式和线程库

98中的一个例子 如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法 #include <algorithm> #include <functional> int main() {int array[] {4,1,8,5,3,7,0,9,2,6};// 默认按照小于比较&#xff0c;排出来结果是升序std::sort(array, a…

文件压缩(Huaffman树的概念及其实现)

什么是压缩 想办法让源文件变得更小并能还原。 为什么要进行文件压缩 文件太大&#xff0c;节省空间提高数据再网络上传输的效率对数据有保护作用—加密 文件压缩的分类 无损压缩 源文件被压缩后&#xff0c;通过解压缩能够还原成和源文件完全相同的格式 有损压缩 解压缩之…

详解STL中的空间配置器(SGI版本)

空间配置器 1.什么是空间配置器 为各个容器高效的管理空间(空间的申请与回收)的 2.为什么需要空间配置器 各种容器----->可以存放元素---->底层需要空间 new 申请空间 operator new ---->malloc调用构造函数------完成对象的构造 动态内存管理总结 前面的容器…

【剑指offer】_15数组中重复的数字

题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的&#xff0c;但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如&#xff0c;如果输入长度为7的数组{2,3,1,0,2,5,3}&#xff0c;那么对应的…

【剑指offer】_16 构建乘积数组

题目描述 给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]A[0]*A[1]*...*A[i-1]*A[i1]*...*A[n-1]。不能使用除法。 解题思路 设有数组大小为5。 对于第一个for循环 第一步&#xff1a;b[0] 1; 第二步&#xff1a;b[1] b[0] * a[0] a[0] 第三…

【剑指offer】_17正则表达式的匹配

题目描述 请实现一个函数用来匹配包括’.‘和*的正则表达式。模式中的字符’.表示任意一个字符&#xff0c;而*表示它前面的字符可以出现任意次&#xff08;包含0次&#xff09;。 在本题中&#xff0c;匹配是指字符串的所有字符匹配整个模式。例如&#xff0c;字符串"aaa…

大四阶段的社会实践的主要目的是_疫情当前,大三大四的学生“很惨”?大一大二的学生也别松懈...

大四毕业生不容易这次疫情对于高校学生而言&#xff0c;可以说是各有各的难处&#xff0c;“这届毕业生很惨”更是屡上热搜。不可否认&#xff0c;大四毕业生确实很不容易&#xff0c;论文答辩、毕业、求职就业等都受到了影响&#xff0c;虽然有困难&#xff0c;但各方都在积极…

【剑指offer】_18 数据流中的中位数

题目描述 如何得到一个数据流中的中位数&#xff1f;如果从数据流中读出奇数个数值&#xff0c;那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值&#xff0c;那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流…

【剑指offer】_19 滑动窗口中的最大值

题目描述 给定一个数组和滑动窗口的大小&#xff0c;找出所有滑动窗口里数值的最大值。例如&#xff0c;如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3&#xff0c;那么一共存在6个滑动窗口&#xff0c;他们的最大值分别为{4,4,6,6,6,5}&#xff1b; 针对数组{2,3,4,2,6,2,…

android 文字反转_多文字共享信息系统

欧阳贵林 www.HeZi.net首发表于2016年03月23日“ 处在信息时代的开端&#xff0c;信息技术不应有特殊的文字性&#xff0c;需要创建多文字共享信息系统&#xff0c;给各国文字一个公平的参与信息与科技创新发展的平台。这是世界的事&#xff0c;更是中国事。”01人类语言语言文…

LeetCode【1--两数之和】 LeetCode【2--两数相加】

两数之和 题目描述 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;你不能重复利用这个数组中同样的元素。 解题思路 直接两…

input数字开头不能为0_李商隐为初恋写诗,每句以数字开头,最后10字一直被仿从未被超越...

上学时&#xff0c;每次写作文&#xff0c;老师总爱在耳边念叨&#xff1a;“你的作文得让阅卷老师看得懂&#xff0c;不然不可能给你高分的&#xff01;”每次听到话&#xff0c;笔者总是用李商隐的诗来和他斗嘴。是的&#xff0c;李商隐的诗作常常是让人读不懂的&#xff0c;…

lsass.exe 当试图更新密码时_“驱动人生”下载器木马再度更新-你应该注意什么?...

360安全大脑监测到通过"驱动人生"供应链攻击传播的挖矿木马在1月30日下午4时左右再次更新。此次更新中&#xff0c;木马在此前抓取系统帐户密码的基础上增加了抓取密码hash值的功能&#xff0c;并试图通过pass the hash攻击进行横向渗透&#xff0c;使得该木马的传播…

LeetCode【3--无重复的最长字串】 LeetCode【4--有序数组中的中位数】

无重复的最长字串 题目描述 给定一个字符串&#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 解题思路 看到这道题&#xff0c;其实就两个步骤&#xff0c;遍历字符串&#xff0c;记录当前字符有没有重复。 重复一般解决就是哈希&#xff0c;这里用个数组表示…

hwt字体转换ttf_五分钟教你弄懂了字体反爬是个啥

今天的文章内容主要是关于字体反爬。目前已知的几个字体反爬的网站是猫眼&#xff0c;汽车之家&#xff0c;天眼查&#xff0c;起点中文网等等。以前也看过这方面的文章&#xff0c;今天跟个老哥在交流的时候&#xff0c;终于实操了一把&#xff0c;弄懂了字体反爬是个啥玩意。…

LeetCode【5--最长的回文子串】 LeetCode【6--Z字形变换】

最长的回文子串 题目描述 给定一个字符串 s&#xff0c;找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 解题思路 可以跟无重复的最长子串一样&#xff0c;用一个滑动窗口&#xff0c;只不过这个窗口的右边界往右&#xff0c;左边界每回要从右边界的下标往左…

androidstudio 日历视图怎么显示农历_中秋国庆旅游攻略怎么做?用这个便签软件很简单...

九月已经到来&#xff0c;中秋节和国庆节距离我们也不远了&#xff0c;今年的中秋和国庆节重叠了有足足八天的假期。不少人都想趁着这个小长假出门旅游&#xff0c;要想保证旅游质量&#xff0c;那么就要做好攻略。中秋国庆旅游攻略怎么做&#xff1f;要想做好一份中秋国庆旅游…

c++ select函数_PySpark 操作函数一览

PySpark 操作函数一览Created: Sep 14, 2020 10:28 AM Tags: Big Data, PySpark, Python, SparkPyspark.sql.functionsfrom pyspark.sql import functions as F函数使用说明基本数学函数类abssin、cos、tan、asin、acos 、atan、sinh、cosh、tanhceil、round、floorexp、log、l…

LeetCode【7--整数反转】 LeetCode【8--字符串转整数】

整数反转 题目描述 给出一个 32 位的有符号整数&#xff0c;你需要将这个整数中每位上的数字进行反转。 解题思路 x%10 取一位&#xff0c;x/10下一位&#xff0c;注意越界&#xff0c; 代码实现 class Solution { public:int reverse(int x) {int sum 0;while(x){if(s…

word2003如何设置护眼模式_ERP系统上线,如何设置采购收货的模式,提升企业的采购效率...

如何合理的规划采购计划上次去拜访一个朋友&#xff0c;他们说公司既然出现没有下达采购订单&#xff0c;供应商也有送货过来的事情&#xff0c;对于公司来说&#xff0c;这个是非常严重的问题。若用了ERP系统之后&#xff0c;如何避免类似的事情发生&#xff0c;今天我们来分享…