1.右值与右值引用
左值:存储在内存中,有明确地址(可取地址)的数据
右值:可以直接提供数据值的数据,不可取地址
可以对表达式取地址(&)的就是左值,所有有名字的变量和对象都是左值;右值是匿名的
//左值
int num = 1;
//左值引用
int& a = num;
//右值
//右值引用
int&& b = 5;
//常量左值引用
const int& c= num;
//常量右值引用
const int&& d = 2;
//const int&& e = b;//error
//const int&& f = d;//error
无论左值引用还是右值引用,都必须初始化,都是取别名,都是为了提升效率;右值引用可以延长右值(临时变量)存活周期;左值引用为了避免指针或者值传递时的内存拷贝
右值分为纯右值(数字、字符串、字面量常量、、lambda表达式、非引用返回的临时变量、运算表达式返回的临时变量)与将亡值(与右值引用有关的表达式,如T&&类型的函数返回值、std::move)
class RightLifecycle
{
public:
//浅拷贝
RightLifecycle() :m_num(new int(100)){
cout << "构造函数 :RightLifecycle " << endl;
cout << "m_num的地址: " <<&m_num<< endl;
}
//拷贝构造函数,深拷贝
RightLifecycle(const RightLifecycle &xx) :m_num(new int(*xx.m_num)) {
cout << "拷贝构造函数 :RightLifecycle " << endl;
}
//移动构造函数(右值引用构造函数)-复用另一个对象的资源(堆内存)
//m_num浅拷贝
RightLifecycle(RightLifecycle&& xx) :m_num(xx.m_num) {
xx.m_num = nullptr;
cout << "移动构造函数 :RightLifecycle " << endl;
}
~RightLifecycle() {
cout << "析构函数 :RightLifecycle " << endl;
delete m_num;
}
int* m_num;
};
RightLifecycle getObj() {
//局部临时变量
RightLifecycle t;
return t;
}
RightLifecycle getObj1() {
//临时匿名对象,将亡值
return RightLifecycle();
}
RightLifecycle&& getObj2() {
//临时匿名对象
return RightLifecycle();
}
//调用
//右侧对象是返回临时变量,移动构造函数才能被调用,否则调用拷贝构造函数
//没有移动构造函数,会调用拷贝构造函数
RightLifecycle objx = getObj();
cout << "普通赋值 m_num的地址: " << objx.m_num << endl;
cout << endl;
RightLifecycle&& obj1x = getObj();
cout << "右值引用 m_num的地址: " << obj1x.m_num << endl;
cout << endl;
//如果没有移动构造函数,使用右值引用的要求会更高些,
//要求右侧对象是临时对象,同时不能去地址的对象,即返回一个临时匿名对象
RightLifecycle&& obj2 = getObj1();
cout << "右值引用 将亡值 m_num的地址: " << obj2.m_num << endl;
cout << endl;
RightLifecycle &&obj3 = getObj2();
cout << "右值引用 将亡值 m_num的地址: " << obj3.m_num << endl;
注意:大量申请资源的类应设计移动构造函数,同时提供构造函数,避免移动构造函数出错
模板参数的T &&与自动推导类型的auto &&表示未定的引用类型
通过右值推导的T&&与auto&&表示右值引用
除右值(左值、左值引用、常量左值引用、右值引用、常量右值引用)推导的T&&
与auto&&表示左值引用
const T &&表示右值引用
template
void ftest_care1(T&& t) {}
void ftest_care2(const T&& t) {}
ftest_care1(10);//右值引用
int x = 10;
ftest_care1(x);//左值引用
ftest_care2(x);//右值引用
int x = 100;
auto && a = x;//左值引用
auto&& b = 3000;//右值引用
const auto&& c = 6;//右值引用
编译器会把已命名的右值引用视为左值,把未命名的优质引用视为右值
右值引用在被推导或者传递后,对应的就是一个左值或者右值
void test_transmit1(int& i) {
cout << "左值引用" << i << endl;
}
void test_transmit1(int&& i) {
cout << "右值引用" << i << endl;
}
void test_transmitT(int&& k) {
test_transmit1(k);
}
//调用
int a = 250;
test_transmit1(a);
test_transmit1(222);
test_transmitT(777);
2.资源转移move
std::move()的作用是初始化右值引用,可以把一个左值转换为右值,是转移没有内存拷贝(与移动构造函数一样,具有移动的意思,把对象状态或者所有权移动到另一个对象)
std::move等效与static_cast
//资源转移,当ls不使用了,要使用ls2时
list ls{ "sad","dsfs","wer" };
list ls1 = move(ls);
//初始化右值引用
int a = 99;
int&& b = move(a);
3.完美转发forward
保证右值引用在参数传递时不会变为左值引用。
forward
template
void printfforward(TF & tf) {
cout<<"左值引用 : " << tf<< endl;
}
template
void printfforward(TF&& tf) {
cout << "右值引用 : " << tf << endl;
}
template
void printfforwardT(TF&& tfT) {
//右值引用传参时,根据情况看变为什么类型,这里是左值
printfforward(tfT);
printfforward(move(tfT));//左值变为右值
printfforward(forward(tfT));//确保不会变换类型
cout << endl;
}
//调用
//forward(t);
//当t为左值引用时,t将会被转换成一个T类型左值
//当t为不为左值时,t将会被转换成一个T类型右值
printfforwardT(520);
int num = 1000;
printfforwardT(num);
printfforwardT(forward(num));//当t为不为左值时,t将会被转换成一个T类型右值
printfforwardT(forward(num));//当t为左值引用时,t将会被转换成一个T类型左值
printfforwardT(forward(num));//当t为不为左值引用时,t将会被转换成一个T类型右值