C++11特性-右值与右值引用

 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(t);当t为左值引用时,t将会被转换成一个T类型左值,当t为不为左值时,t将会被转换成一个T类型右值

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类型右值

你可能感兴趣的:(#,C++,c++)