C++11之右值以及右值引用

Posted 遥远的歌s

tags:

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

右值的介绍

在引入右值之前,我们先来了解一下左值,左值在日常代码中非常常见,我们定义的变量大多数都是左值。简单理解就是,左值既可以出现在=的两边,或者是可以取地址的变量
比如:

void testL()

	int a = 10;//a就是一个左值
	int b = a;//b也是一个左值
	//因为a,b是左值,所以可以取它门的地址
	int *pa = &a;
	int *pb = &b;

那么与他对应起来的右值便是:只能出现在=的右边,或者不可以取地址这里并不是绝对的,但是可以做一个区分,比如上述代码中:

void testL()

	int a = 10;//10是一个右值
	int b = a;//这里a并不是一个右值,因为a在上面的定义中为左值
	//10 = 20;这是不对的,因为10是一个右值
	//int *p = &10;这也是不对的,因为10是一个右值,他不能被取地址

所以有了上面的了解,再看C++中定义右值:
右值:

  1. 纯右值:即常量,临时变量(比如一个函数的返回值)
  2. 将亡值:生命周期即将结束
  3. 不是右值的都是左值

比如:

//临时变量:函数以值返回的变量,调用类的构造函数创建的变量
int getA(int a)

	return a;//返回a

void test()

	int a = 10;
	int b = a;
	getA(a);//这个函数的返回值是一个临时变量,它可以被其它变量接收,但是不能出现在=的左边,也不能取地址。例如:
	//getA(a) = b;是没有这样的语法的
	//int *p = &(getA(a));  这也是不对的

将亡值:

int getA(int a)

	return a;//这里的a作为返回值,要创建一个拷贝,所以a本身就是一个将亡值,即在该函数结束时这个值就会挂掉

右值的引用

我们知道引用:变量的别名
而左值引用,即一个&符号后面跟上一个变量名。
左值引用:引用的实体既可以是左值,也可以为右值

void test()

	int a;//a是一个左值
	int& ra = a;//左值引用
	const int& ri = 10;//ri实体为右值,这里必须加const否则不能这样用

右值引用:引用的实体只能是右值,不能引用左值
右值引用符号为 & &
比如:

int getA(int a)

	return a;//返回a

void test()

	int a = 10;
	int&& lr = 10;//纯右值引用
	int&& lr2 = getA(a);//引用函数的返回值
	//如果想要用左值来引用,则需要加一个const
	const int& r = getA(a);

再比如:

class D

public:
	D()
	
		cout<<"D(int)"<<endl;
	
	D(const D& d)
	
		cout<<"const D& d"<<endl;
	
;
void test()

	D&& rd = D();//右值引用
	//编译器有优化,这里实际上是先调用构造函数后在调用拷贝构造函数
	const D& rd2 = D();//左值引用右值


右值的作用其实在于拷贝
下面举个例子:

class String

public:
	String(char* str = "")
		:_str(new char[strlen(str) + 1])
		,_size(strlen(str))
		,_capacity(strlen(str));
	
		strcpy(_str,str);
	
	String(const String& str)
		:_str(new char[strlen(str._str) + 1])//深拷贝需要开空间取拷贝,因此运行效率就会降低
	
		strcpy(_str,str._str);
		_size = _capacity = strlen(str._str);
		cout<<"String(const String& str)"<<endl;
	
private:
	char* _str;
	int _size;
	int _capacity;
;
String getString()

	String str("123");//第一次拷贝构造
	return str;//将亡值

void test()

	//第二次拷贝构造,赋值给一个匿名对象,然后将亡值销毁
	//第三次拷贝构造,匿名对象赋值给ret
	String ret = getString();
	//到这里结束会调用三次拷贝构造

上述代码中getString()函数中,str变量是一个将亡值,他会在该函数生命周期结束的时候,被系统回收,但是在test()函数中,要赋值给ret变量,此时,这个将亡值需要拷贝一份,然后再把这个拷贝的值赋给ret,也就是说要多走一次拷贝,这大大的降低了代码的效率,因此,这里使用右值引用就会提高代码的运行效率。
通俗理解:比如这有一部手机,我非要让厂家按照这部手机一模一样给我再造一个出来供我使用,而原先的那部手机我不要直接扔掉,如果按照右值引用的拷贝构造,则我可以直接用这一部手机,而不需要让厂家给我再造一个一模一样的,这样,效率就会得到很大的提升。
右值引用的拷贝构造

String(String&& str)
		:_str(str._str)//不需要开空间进行深拷贝,直接拿来用
	
		str._str = nullptr;//资源已经给我了,不需要他自己释放,我来释放就可以,因此这里给nullptr
		_size = _capacity = str._size;//直接拿来用
		cout<<"String(String&& str)"<<endl;
	

上述右值引用的拷贝构造中,参数str明确不会再被利用,函数结束后没有谁会再用,因此,_str直接拿过来利用即可。

class String

public:
	String(char* str = "")
		:_str(new char[strlen(str) + 1])
		,_size(strlen(str))
		,_capacity(strlen(str));
	
		strcpy(_str,str);
	
	String(const String& str)//深拷贝
	
		strcpy(_str,str._str);
		_size = _capacity = strlen(str._str);
		cout<<"String(const String& str)"<<endl;
	
	String(String&& str)//右值引用拷贝构造
		:_str(str._str)
	
		str._str = nullptr;
		_size = _capacity = str._size;
		cout<<"String(String&& str)"<<endl;
	
private:
	char* _str;
	int _size;
	int _capacity;
;
String getString()

	String str("123");//第一次拷贝构造
	return str;//将亡值

void test()

	String ret = getString();//这里的ret直接用getString函数中str的资源,因为getString函数结束后,str不会再被用到
	String copy(ret);//这个则不是右值拷贝构造,因为变量ret还会被用到,所以这里会走深拷贝

因为编译器有优化,不会打印拷贝构造中的语句,故在这里不展示运行结果。
总结一下

  1. 右值引用和左值引用都是变量的别名
  2. 左值引用可以引用右值也可以引用左值,但是引用右值的时候需要加上const修饰。而右值引用只能引用右值
  3. 右值引用最大作用是标记右值引用指向的实体(右值)资源可以直接被拿走,从而提高拷贝的效率,

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

❥关于C++之右值引用&移动语义┇移动构造&移动复制

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

C++11 左值右值右值引用详解

[转]C++11 左值右值右值引用详解

c++11:左值右值

c++11的右值引用移动语义