小言_互联网的博客

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

556人阅读  评论(0)

左值与右值

在C语言中,左值和右值一般有两种区分的方法。可以出现在赋值符号“=”的两边的值为左值,只能出现在赋值符号“=”的右边的值为右值;还有一种说法是能取地址的为左值,不能取地址的为右值。但是这两种说法并非完全正确

void test()
{
   
	int a = 10;
	int b = a;//ok a为左值
	10 = a; //error 10为右值

	int* pa = &a;//ok
	int* pi = &10;//error
}

而在C++中,右值有3种变量,分别为常量临时变量/匿名变量将亡值;其他的变量都为左值

将亡值就是声明即将结束的变量

int fun(int a)
{
   
	return a;//将亡值
}

C++引入右值的原因

  1. 实现移动语义(移动构造和移动赋值)
  2. 给中间临时变量取名字
  3. 实现完美转发

左值引用和右值引用

左值引用就是我们平时用的引用,左值引用就是在类型后面加&,则定义该变量为引用类型。左值引用及可以引用左值,也可以引用右值

void test()
{
   
	int a = 10;
	int& ra = a;//引用左值
	const int& ri = 10;//引用右值
}

右值引用则需要在变量类型加上&&,则为右值引用。右值引用不能引用左值,只能引用右值

void test()
{
   
	int a = 10;
	int&& rri = 10;//ok
	int&& rra = a;//error;
}

右值引用的作用

在我们平时写的代码中,常常为有创建拷贝以及释放空间的开销,而C++是一门追求极致性能的语言,所以会尽可能在保持原有特性上增强性能。而引入右值引用,目的就是为了在某些特定场景下提高代码运行的效率—通过不进行深拷贝来提高代码拷贝效率
我们先来看看自己实现的string类

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class String {
   
public:
	String(const char* str = "") {
   
		if (nullptr == str) 
			str = "";
		_str = new char[strlen(str) + 1]; strcpy(_str, str);
	}
	String(const String& s) : 
		_str(new char[strlen(s._str) + 1])
	{
   
		strcpy(_str, s._str);
		cout << "String(const String&)" << endl;
	}
	String& operator=(const String& s) {
   
		if (this != &s) {
   
			char* pTemp = new char[strlen(s._str) + 1]; 
			strcpy(pTemp, s._str); 
			delete[] _str; _str = pTemp;
			cout << "String& operator=(const String&)" << endl;
		} return *this;
	}
	String operator+(const String& s) {
   
		char* pTemp = new char[strlen(_str) + strlen(s._str) + 1]; 
		strcpy(pTemp, _str); 
		strcpy(pTemp + strlen(_str), s._str); 
		String strRet(pTemp);
		return strRet;
	}
	~String() 
	{
    
		if (_str) delete[] _str; 
	}
private: char* _str;
};
void test() {
   
	String s1("hello"); 
	String s2("world"); 
	String s3(s1 + s2); 
}

在测试代码中,要创建s3会经历很多次的申请空间,第一是先要先将s1和s2进行+操作,在operator+函数中,最后要将空间赋值给strRet是时会进行拷贝,然后在返回strRet时也会进行一次拷贝,然后将拷贝的临时变量用来创建s3时又要调用拷贝构造。而拷贝构造是深拷贝,每次调用都需要开辟空间和花费时间

但是在vs中编译器会进行优化。将返回值那一步拷贝过程去掉


但是在C++11中,引入了 右值引用,还可以有优化的空间。也就是s3也不需要开辟新的空间,直接利用strRet的原有资源。将strRet中的资源移动到s3中

	String(String&& s) 
		:_str(s._str)//直接指向strRet的资源
	{
   
		s._str = nullptr;//置其为空,防止二次释放
		cout << "String(String&&)" << endl;
	}

在右值引用的拷贝构造中并没有申请新的空间。这两个拷贝构造可以共存,大部分情况下是会调用左值引用的拷贝构造,只有以上情况,也就是临时变量,只有为右值时才会调用右值引用的拷贝构造。
同理,重载赋值运算符也可以使用右值引用

	String& operator=(String&& s) {
   
		if (this != &s) {
   
			delete[] _str;
			_str = s._str;
			s._str = nullptr;
			cout << "String& operator=(String&&)" << endl;
		} return *this;
	}


move----将左值属性的变量修改为右值

void test()
{
   
	int a = 10;
	int&& rra = move(a);//ok
}

完美转发----在函数传递过程中保持变量的原有属性

void Fun(int& x) {
    cout << "左值引用" << endl; }
void Fun(int&& x) {
    cout << "右值引用" << endl; } 
void Fun(const int& x) {
    cout << "const类型的左值引用" << endl; } 
void Fun(const int&& x) {
    cout << "const类型的右值引用" << endl; }
template<typename T> 
void PerfectForward(T&& t) //如果是左值则为左值引用
{
    
	Fun(std::forward<T>(t)); //完美转发std::forward<T>(name)
}
void test() {
   
	PerfectForward(10); // 右值引用
	int a = 4;
	PerfectForward(a); // 左值引用
	PerfectForward(std::move(a)); // 右值引用
	const int b = 8;
	PerfectForward(b); // const类型的左值引用
	PerfectForward(std::move(b)); // const类型的右值引用
}


转载:https://blog.csdn.net/qq_44443986/article/details/117405080
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场