C++ C++11新特性--右值引用

Posted WhiteShirtI

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ C++11新特性--右值引用相关的知识,希望对你有一定的参考价值。

左值与右值

在C语言中,左值和右值一般有两种区分的方法。可以出现在赋值符号“=”的两边的值为左值,只能出现在赋值符号“=”的右边的值为右值;还有一种说法是能取地址的为左值,不能取地址的为右值。但是这两种说法并非完全正确

void test()

	int a = 10;
	int b = a;//ok a为左值
	10 = a; //error 10为右值

	int* pa = &a;//ok
	int* pi = &10;//error

而在C++中,右值有3种变量,分别为常量临时变量/匿名变量将亡值;其他的变量都为左值

将亡值就是声明即将结束的变量

int fun(int a)

	return a;//将亡值

C++引入右值的原因

  1. 实现移动语义(移动构造和移动赋值)
  2. 给中间临时变量取名字
  3. 实现完美转发

左值引用和右值引用

左值引用就是我们平时用的引用,左值引用就是在类型后面加&,则定义该变量为引用类型。左值引用及可以引用左值,也可以引用右值

void test()

	int a = 10;
	int& ra = a;//引用左值
	const int& ri = 10;//引用右值

右值引用则需要在变量类型加上&&,则为右值引用。右值引用不能引用左值,只能引用右值

void test()

	int a = 10;
	int&& rri = 10;//ok
	int&& rra = a;//error;

右值引用的作用

在我们平时写的代码中,常常为有创建拷贝以及释放空间的开销,而C++是一门追求极致性能的语言,所以会尽可能在保持原有特性上增强性能。而引入右值引用,目的就是为了在某些特定场景下提高代码运行的效率—通过不进行深拷贝来提高代码拷贝效率
我们先来看看自己实现的string类

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class String 
public:
	String(const 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);
		cout << "String(const String&)" << endl;
	
	String& operator=(const String& s) 
		if (this != &s) 
			char* pTemp = new char[strlen(s._str) + 1]; 
			strcpy(pTemp, s._str); 
			delete[] _str; _str = pTemp;
			cout << "String& operator=(const String&)" << endl;
		 return *this;
	
	String operator+(const String& s) 
		char* pTemp = new char[strlen(_str) + strlen(s._str) + 1]; 
		strcpy(pTemp, _str); 
		strcpy(pTemp + strlen(_str), s._str); 
		String strRet(pTemp);
		return strRet;
	
	~String() 
	 
		if (_str) delete[] _str; 
	
private: char* _str;
;
void test() 
	String s1("hello"); 
	String s2("world"); 
	String s3(s1 + s2); 

在测试代码中,要创建s3会经历很多次的申请空间,第一是先要先将s1和s2进行+操作,在operator+函数中,最后要将空间赋值给strRet是时会进行拷贝,然后在返回strRet时也会进行一次拷贝,然后将拷贝的临时变量用来创建s3时又要调用拷贝构造。而拷贝构造是深拷贝,每次调用都需要开辟空间和花费时间

但是在vs中编译器会进行优化。将返回值那一步拷贝过程去掉


但是在C++11中,引入了 右值引用,还可以有优化的空间。也就是s3也不需要开辟新的空间,直接利用strRet的原有资源。将strRet中的资源移动到s3中

	String(String&& s) 
		:_str(s._str)//直接指向strRet的资源
	
		s._str = nullptr;//置其为空,防止二次释放
		cout << "String(String&&)" << endl;
	

在右值引用的拷贝构造中并没有申请新的空间。这两个拷贝构造可以共存,大部分情况下是会调用左值引用的拷贝构造,只有以上情况,也就是临时变量,只有为右值时才会调用右值引用的拷贝构造。
同理,重载赋值运算符也可以使用右值引用

	String& operator=(String&& s) 
		if (this != &s) 
			delete[] _str;
			_str = s._str;
			s._str = nullptr;
			cout << "String& operator=(String&&)" << endl;
		 return *this;
	


move----将左值属性的变量修改为右值

void test()

	int a = 10;
	int&& rra = move(a);//ok

完美转发----在函数传递过程中保持变量的原有属性

void Fun(int& x)  cout << "左值引用" << endl; 
void Fun(int&& x)  cout << "右值引用" << endl;  
void Fun(const int& x)  cout << "const类型的左值引用" << endl;  
void Fun(const int&& x)  cout << "const类型的右值引用" << endl; 
template<typename T> 
void PerfectForward(T&& t) //如果是左值则为左值引用
 
	Fun(std::forward<T>(t)); //完美转发std::forward<T>(name)

void test() 
	PerfectForward(10); // 右值引用
	int a = 4;
	PerfectForward(a); // 左值引用
	PerfectForward(std::move(a)); // 右值引用
	const int b = 8;
	PerfectForward(b); // const类型的左值引用
	PerfectForward(std::move(b)); // const类型的右值引用

以上是关于C++ C++11新特性--右值引用的主要内容,如果未能解决你的问题,请参考以下文章

C++基础(插入1)——C++11新特性:右值引用移动语义完美转发

C++11右值引用(一看即懂)

如何评价 C++11 的右值引用(Rvalue reference)特性?

C11新特性右值引用&&

C++ 11特性

C++ 11特性