C++11右值引用

C++11右值引用

1、什么是左值?什么是右值?

  左值是表达式结束后依然存在的对象;右值是表达式结束时就不再存在的对象。

2、在早期的C++中,普通 & 无法对右值取引用。只有 const & 可以对右值取引用。

const int& i = 10;

  

  右值引用能够解决两个问题。

    1)非必要的拷贝操作
    2)模板函数,按照实际类型进行转发

3、第一大特点:通过右值引用的声明,右值又“重获新生”

#include <iostream>
using namespace std;

int g_constructCount=0;
int g_copyConstructCount=0;
int g_destructCount=0;
struct A
{
    A(){
        cout<<"construct: "<<++g_constructCount<<endl;    
    }
    
    A(const A& a)
    {
        cout<<"copy construct: "<<++g_copyConstructCount <<endl;
    }
    ~A()
    {
        cout<<"destruct: "<<++g_destructCount<<endl;
    }
};

A GetA()
{
    return A();
}

int main() {
    A a = GetA();
    return 0;
}
View Code

    上面代码,会产生如下输出 :

construct: 1
copy construct: 1
destruct: 1
copy construct: 2
destruct: 2
destruct: 3

    如果将 A a  = GetA(); 改为下在这样,会产生编译错误。

A& a = GetA()

  改为 A&& a = GetA() 是可以的。会产生如下输出 :

construct: 1
copy construct: 1
destruct: 1
destruct: 2

4、第二大特点,右值引用本身可能是左传,也可能是右值。

template<typename T>
void f(T&& t){}

f(10); //t是右值

int x = 10;
f(x); //t是左值

5、第三大特点,universal references. T&& t在发生自动类型推断的时候,如果被一个左值初始化,它就是一个左值;如果它被一个右值初始化,它就是一个右值,它是左值还是右值取决于它的初始化。

template<typename T>
void f(T&& param); 

template<typename T>
class Test {
    Test(Test&& rhs); 
};

6、为了避免 copy constructor 中内存很大的问题,C++11中新增了 move constructor.

  copy constructor 中做深拷贝。 move constructor 中做浅拷贝,并且将参数中的数据reset。

class A
{
public:
    A() :m_ptr(new int(0)){}
    A(const A& a):m_ptr(new int(*a.m_ptr)) //深拷贝的拷贝构造函数
    {
        cout << "copy construct" << endl;
    }
    A(A&& a) :m_ptr(a.m_ptr)
    {
        a.m_ptr = nullptr;
        cout << "move construct" << endl;
    }
    ~A(){ delete m_ptr;}
private:
    int* m_ptr;
};
int main(){
    A a = Get(false); 
} 
construct
move construct
move construct

7、std::move的作用是什么?

  

  std::move用于把任意类型转化为右值引用,以方便调用move构造函数/move赋值函数。

8、std::forward的作用是什么?把左值引用还原为左值,把右值引用还原为右值,即把尽可能把值变变右值。

template <typename T>
void forwardValue(T& val)
{
    processValue(val); //右值参数会变成左值 
}
template <typename T>
void forwardValue(const T& val)
{
    processValue(val); //参数都变成常量左值引用了 
}

  如果调用 fworwardValue(0),则 processValue(val)时,参数变成了左值引用。

  forward<T>的目的是保护引用类型。

 3、四大类型

    1)non-const lvalue

    2)const lvalue

    3)non-const rvalue

    4)const rvalue

 4、为什么non-const lvalue只能绑定到本类对象?

    首先因为const的限制non-const只能绑定到non-const,因此排除(2)、(4),再者如果绑定到non-const rvalue,则有可能导致在后续修改rvalue,这显然有巨大风险。

 5、为什么non-const rvalue只能绑定到本类对象?

    如果允许绑定到non-const lvalue,则有可能导致数据被窃取;如果绑定到const rvalue,const lvalue则有可能修改原数据。

 6、template<typename T >

    void f(T&& t){ g(t); }

  我们规定:

    如果实参类型为右值,那么T&&就被推导为右值引用。

    如果实参类型为左值,那么T&&就被推导为左值引用。

 7、有了右值引用后,需要添加以下类成员函数:

  1)右值引用构造函数。

  2)右值引用拷贝构造函数。

 参考:

 1、http://www.cnblogs.com/likaiming/p/9029908.html

 2、http://blog.csdn.net/pongba/article/details/1697636

原文地址:https://www.cnblogs.com/tekkaman/p/3496773.html