理解C++中的右值引用
右值引用,顾名思义,就是对一个右值进行引用,或者说给右值一个别名。右值引用的规则和左值一用一模一样,都是对一个值或者对象起个别名。
1. 右值引用和左值引用一样,在定义的同时必须立即赋值,如果不立即赋值,语法错误,看下面的例子
class A
{
public:
int m_val;
A(int n):m_val(n) { }
A(A&& a)
{
m_val=a.m_val;
cout<<"move constructor is called"<<endl;
}
A& operator=(const A&r)
{
if(this!=&r)
{
m_val=r.m_val;
cout<<"move assignment operator is called"<<endl;
}
}
~A()
{
cout<<"Destructor is called"<<endl;
}
};
int main(int argc, char const *argv[])
{
A a(10); //定一个对象(左值)
A& lr=a; //左值引用,同时赋值
A&& rr=move(a); //定义右值引用,同时赋值
A&& rr2; //定义右值引用,但是没有赋值
rrw=move(a); //非法
return 0;
}
2. 右值引用的操作和左值引用一样,操作右值引用,就是操作源对象本身,因为右值引用就是源对象的一个别名。看下面的例子,操作左值引用、右值引用、源对象中的任何一个,其它两个都相应变化。
class A
{
public:
int m_val;
A(int n):m_val(n) { }
A(A&& a)
{
m_val=a.m_val;
cout<<"move constructor is called"<<endl;
}
A& operator=(const A&r)
{
if(this!=&r)
{
m_val=r.m_val;
cout<<"move assignment operator is called"<<endl;
}
}
~A()
{
cout<<"Destructor is called"<<endl;
}
};
int main(int argc, char const *argv[])
{
A a(10); //定一个对象(左值)
A& lr=a; //左值引用,同时赋值
A&& rr=move(a); //定义右值引用,同时赋值
cout<<"lr.m_val="<<lr.m_val<<" "<<"rr.m_val="<<rr.m_val<<" "<<"a.m_val="<<a.m_val<<endl;
cout<<"change value by original object"<<endl;
a.m_val=20;
cout<<"lr.m_val="<<lr.m_val<<" "<<"rr.m_val="<<rr.m_val<<" "<<"a.m_val="<<a.m_val<<endl;
cout<<"change value by left reference"<<endl;
lr.m_val=30;
cout<<"lr.m_val="<<lr.m_val<<" "<<"rr.m_val="<<rr.m_val<<" "<<"a.m_val="<<a.m_val<<endl;
cout<<"change value by right reference"<<endl;
rr.m_val=40;
cout<<"lr.m_val="<<lr.m_val<<" "<<"rr.m_val="<<rr.m_val<<" "<<"a.m_val="<<a.m_val<<endl;
cout<<"left reference and right reference won't create any new object"<<endl;
return 0;
}
输出结果如下:再次证明,引用就是起别名
3. 从上图可以看出,引用不会产生任何新的对象。
4. 右值引用和移动构造函数、移动赋值运算符没有任何关系。
5. 右值引用被正确赋值以后,还能被二次赋值,引用到一个新的对象上吗?不能。右值引用一旦被定义,随后的操作就对源对象的操作了。看下面这个例子:
class A
{
public:
int m_val;
A(int n):m_val(n) { }
A(A&& a)
{
m_val=a.m_val;
cout<<"move constructor is called"<<endl;
}
A& operator=(const A&r)
{
if(this!=&r)
{
m_val=r.m_val;
cout<<"move assignment operator is called"<<endl;
}
}
~A()
{
cout<<"Destructor is called"<<endl;
}
};
int main(int argc, char const *argv[])
{
A a1(10);
A a2(20);
A&& rf=move(a1);
rf=move(a2); //不是对右值引用进行新的引用,而是等价为:a1=move(a2)
cout<<"rf.m_val="<<rf.m_val<<" "<<"a2.m_val= "<<a2.m_val<<endl;
a2.m_val=30;
cout<<"rf.m_val="<<rf.m_val<<" "<<"a2.m_val= "<<a2.m_val<<endl;
return 0;
}
rf=move(a2);不是二次引用,而是等价为a1=move(a2),因为rf就是a1,这里一个移动赋值运算符被调用了
牢牢记住,右值引用就是给右值(纯右值、将亡值)起个别名,延长右值的生命周期,没看到多大的使用价值。
另外,定义右值引用的时候,经常看到move函数,但是右值引用和move没有任何关系,move的作用是把一个左值强制转换为右值