结论:
-
std:move的作用是把左值引用强转为右值引用
-
右值引用的作用是在某些场景下降低开销
-
右值引用可以理解为:把本来即将消亡的临时变量继续使用
正文:
-------------------------------------------------------------------------------------------------------------------
左值和右值
判断左值和右值的方法有两种
1.在等号左边的值就称为左值而在等号右边的称为右值
2.另外在c++中还有一种判别方法就是可以取地址,有名的就是左值,不能取地址,没有名的就是右值
例如:
a = 100
a在等号左边被值为左值
100在等号右边被称为右值
左值引用、右值引用
左值引用:
int d = 100;
int & dd = d;
dd是左值引用,相当于变量d的别名。
右值引用:
int && c = RetValue();
c是右引用,相当于给函数RetValue()的返回值取了个别名。RetValue()在函数返回右值表达式结束后,他的生命也就终结了,而右值引用的声明,又给他“重获新生”,他的生命周期将与他的右值引用c的生命周期一样,只要c还活前些,该右值临时变量都会一直存活下去
无法将一个左值引用绑定一个右值
int & d = 100;//不可以,编译错误:100是一个右值,无法绑定到左值引用
也不可以将右值引用绑定到左值
int d = 100;
int && dd = d;//无法通过编译:d是一个左值,无法绑定到右值引用
如果非要把右值引用绑定到左值呢?可以这样:
int d = 100;
int && dd = std::move(d); //将左值d强制转换为右值
当然,c98有一种常左值引用就是const T&,这在c98里是“万能”引用类型,他可以接受,非常量左值,常量左值,右值对其初始化例如:
int d = 100;
const int e = 100;
const int & c = d;//接受一个非常量左值
const int & v = e;//接受一个常量左值
const int & z = 3 + 4;//接受一个右值
【std::move】:强制使左值转成右值
请注意,用std::move转换过的变量不要再使用了
【例1】:
#include <iostream>
#include <string>
void main(void){
std::string strA = "Hello";
std::cout << "HHHA:0====>strA=\"" << strA << "\"\n";
std::string strB = std::move(strA);
std::cout << "HHHA:1====>strA=\"" << strA << "\"\n";
std::cout << "HHHA:2====>strB=\"" << strB << "\"\n";
}
【输出】:
【解释】:
语句std::move(strA) 把变量strA强制转成了右值,strA所指向的实体将随着赋值语句的结束而消亡。对strB而言,等效于:
std::string strB = "Hello";
【C++中引用的作用】降低临时变量开销
无论是左值引用还是右值引用,其目的都是为了降低开销。常规参数会在函数调用时调用参数的构造函数,如:
void Func_01(CTestObj a)
{
...;
}
每次调用Func_01的时候都会调用CtestObj的构造函数,其相关开销取决于CTestObj本身,它除要做以下事情:
1.为CTestObj分配空间;
2.调用CTestObj的构造函数;
3.函数调用结束后释放为CTestObj分配的空间。
引用的使用可以把上面这三件事都省掉。
常量左值引用做函数参数(c98):
int Add(const T & s1,const T & s2)
{
...
}
用右值引用来做函数的参数(c++ 11):
//在函数内部还可以修改引用a 的值
void Test(CTestObj && a);
{
//可以修改引用a 的值
...
}
【std:move的使用】
【例2】
#include <iostream>
#include <string>
class HugeMem
{
public:
HugeMem(int size) : sz(size > 0 ? size : 1)
{
c = new int[sz];
}
~HugeMem()
{
delete [] c;
}
HugeMem(HugeMem && hm) : sz(hm.sz), c(hm.c)
{
hm.c = nullptr;
}
int *c;
int sz;
};
class ClassA
{
public:
ClassA():i(new int(3)),h(1024) {} //P1:常规构造函数
~ClassA() { delete i;}
ClassA(ClassA && s) : i(s.i), h(std::move(s.h)) //P2:右值引用为参数的构造函数
{
s.i = nullptr; //P3:防止误操作
}
HugeMem h;
int * i;
};
ClassA Func_02()
{
ClassA temp = ClassA(); //P4:调用ClassA的常规构造函数,见P1
std::cout << std::hex << "HHHA:0====>huge mem:" << " @" << temp.h.c << std::endl;
return temp; //P5: temp是该函数的临时变量
}
void main()
{
ClassA a(Func_02()); //P6:调用ClassA右值引用为参数的构造函数
std::cout << std::hex << "HHHA:1====>huge mem:" << " @" << a.h.c << std::endl;
}
【输出】:
【解释】:
Func_02内部构造了一个临时变量temp并返回,main函数中用这个返回的临时变量为参数构造另一个变更a。而ClassA中以右值引用为参的构造函数把临时变量的实体的成员分别转给了新变量的成员。运行结果显示temp和a两个变量的.h.c的地址相同。
参考:https://yq.aliyun.com/articles/568097?spm=a2c4e.11155472.0.0.2a3f4cd2gYOkIJ