右值引用?

什么是右值?

rvalue是read value的缩写:右值是指可以提供数据值的数据(不可取地址)

可以用表达式取地址(&)的就是左值,否则就是右值。

C++11中的右值分为两种:一种是纯右值,一种是将亡值。

纯右值:非引用返回的临时变量,运算符表达式产生的临时变量,原始字面量和lambda表达式等

将亡值:与右值引用相关的表达式,例如:T&&类型函数的返回值,std::move的返回值

什么是右值引用?

右值引用就是对一个右值进行引用的类型。因为右值是匿名的所有我们只能用过引用的方式来找到它。

无论申明左值引用还是右值引用都必须进行初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用的申明,该右值又重获新生。

    //左值
    int num = 10;
    //左值引用
    int& a = num;
    //右值引用
    int&& b = 8;
    //常量右值引用
    const int&& c = 8;
    //常量左值引用
    const int& d = num;
    const int& e = b;
    const int& f = c;
    const int& g = d;

右值引用具有移动语义,移动语义可以将资源(堆,系统对象等)通过浅拷贝的方式从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建,拷贝以及销毁,可以大幅提高c++应用程序的性能。

#include 
using namespace std;

class Test
{
public:
    Test() : m_num(new int(100))
    {
        cout << "construct: my name is jerry" << endl;
        printf("m_num地址: %p\n", m_num);
    }

    Test(const Test& a) : m_num(new int(*a.m_num))
    {
        cout << "copy construct: my name is tom" << endl;
    }

    // 添加移动构造函数
    Test(Test&& a) : m_num(a.m_num)
    {
        a.m_num = nullptr;
        cout << "move construct: my name is sunny" << endl;
    }

    ~Test()
    {
        delete m_num;
        cout << "destruct Test class ..." << endl;
    }

    int* m_num;
};

Test getObj()
{
    Test t;
    return t;
}

Test getObj1()
{
    return Test();
}

int main()
{
    Test t = getObj();
    printf("m_num地址: %p\n", t.m_num);
    cout << endl;
    Test&& t1 = getObj1();
    printf("m_num地址: %p\n", t1.m_num);
    //cout << "t.m_num: " << *t.m_num << endl;
    return 0;
};

结果如下: 

construct: my name is jerry
m_num地址: 01200DC8
move construct: my name is sunny
destruct Test class ...
m_num地址: 01200DC8

construct: my name is jerry
m_num地址: 011F7160
m_num地址: 011F7160
destruct Test class ...
destruct Test class ...

在上面的代码中给Test类中添加了移动构造函数(参数为右值引用类型),这样在进行Test t = getObj()操作的时候并没有调用拷贝构造函数进行深拷贝,而是调用了移动构造函数,这个函数中只是进行了浅拷贝,没有对临时对象进行深拷贝,提高了性能。

getObj是一个将亡值,也就是说是一个右值,在进行赋值操作的时候如果 = 右边是一个右值,那么移动构造函数就会被调用。移动构造函数中使用了右值引用,会将临时对象中的堆内存地址的所有权转移给对象t,这块内存被成功续命,因此在t对象中还可以继续使用这块内存。

如果没有移动构造函数,使用右值引用初始化要求更高一些,要求右侧是一个临时的不能去地址的对象,就是上面的getObj1()函数。

你可能感兴趣的:(开发语言)