`C++11`右值引用

75 阅读3分钟

左值和左值引用

什么是左值?什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),可以获取它的地址、也可以对它赋值, 左值可以出现在赋值符号左边,可以出现在赋值符号右边(如左值引用)。右值不能出现在赋值符号左边。

左值引用就是给左值的引用,给左值起别名。

// 左值和左值引用
int main()
{
	// a,b,c都是左值,*b也是左值
	int a = 1;
	int* b = new int;
	const int c = 10;

	// 左值引用
	int& ra = a;
	int*& rb = b;
	const int& rc = c;
	int& rp = *b;
   
	return 0;
}

右值和右值引用

什么是左值?什么是左值引用?

右值也是一个数据的表达式,如:字面常量,表达式返回值,函数返回值(传值返回中间的临时变量)等。右值出现在赋值符号的右边,不能出现在赋值符号的左边,不能取地址。

右值引用就对右值的引用,给右值起别名。

// 右值和右值引用
int main()
{
	int x, y;

	// 下面就是常见的右值
	1;
	x + y;
	fun(x, y);  //返回时中间的临时变量

	// 右值引用
	int&& r1 = 1;
	int&& r2 = x + y;
	int&& r3 = func(x, y);
    
	return 0;
}

两者比较

左值引用总结:

  1. 左值引用只能引用左值,不能引用右值。
  2. const左值引用既可以引用左值,又可以引用右值。
int main()
{
	int x, y;
    
	// const左值引用可以引用右值
	int& ret = (x + y); // 报错
	const int& ret = (x + y);
}

右值引用总结:

  1. 右值引用只能引用右值,不能引用左值。
  2. 右值引用可以引用move后的左值。
int main()
{
	int a; 
	// 右值引用不可以给左值起别名
	int&& ref1 = a;

	//右值引用可以给move后的左值起别名
	int&& ref2 = move(a);
    
    return 0;
}

应用

C++提出左值引用是为了提高效率,比如传引用传参,传引用返回,目的就是为了减少拷贝。尤其对于自定义类型来说,拷贝的代价很大。。那C++11提出右值引用又是为了什么? 其实还是为了提高效率。来看下面代码:

class A
{
public:
	A()
		:_a(new char)
	{}

	A(const A& a)
	{
		cout << "A(const A& a) ---- 深拷贝" << endl;
		_a = new char;
	}

	void swap(A& a)
	{
		::swap(_a, a._a);
		::swap(_b, a._b);
	}

	A(A&& a)
		:_a(nullptr)
	{
		cout << "A(A&& a)  ---- 移动构造" << endl;
		swap(a);
	}

	A& operator+=(int x)
	{
		_b += x;

		return *this;
	}

	A operator+(int x)
	{
		A tmp(*this);
		tmp += x;
		return tmp;
	}

private:
	char* _a;
	int _b = 1;
};


int main()
{
	A a1;
	A a2 = a1;
	A a3 = (a2 + 1);
	return 0;
}

无移动构造函数时:

可以看到,return tmp的时候要调用拷贝构造,那必然会开辟额外的空间,对于深拷贝来说是很大的消耗。但是此时tmp是一个将亡值,如果我们可以将它的资源给转移走,就不用开辟额外空间。

image-20240223202748380

image-20240223202106341

image-20240223204221918

添加移动构造:

可以看到,在return tmp的时候在没有发生拷贝,只是将它的资源转移走。

image-20240223204803651

image-20240223204840056

image-20240223204911733

~~接着更