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++中定义右值:
右值:
- 纯右值:即常量,临时变量(比如一个函数的返回值)
- 将亡值:生命周期即将结束
- 不是右值的都是左值
比如:
//临时变量:函数以值返回的变量,调用类的构造函数创建的变量
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还会被用到,所以这里会走深拷贝
因为编译器有优化,不会打印拷贝构造中的语句,故在这里不展示运行结果。
总结一下
- 右值引用和左值引用都是变量的别名
- 左值引用可以引用右值也可以引用左值,但是引用右值的时候需要加上const修饰。而右值引用只能引用右值
- 右值引用最大作用是标记右值引用指向的实体(右值)资源可以直接被拿走,从而提高拷贝的效率,
以上是关于C++11之右值以及右值引用的主要内容,如果未能解决你的问题,请参考以下文章