Bootstrap

理解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的作用是把一个左值强制转换为右值,具体参考这里:真正理解std::move-CSDN博客

;