【C++右值引用】移动构造和移动赋值,万能引用,右值详讲

目录

1.右值和左值

2.左值引用和右值引用 

3.移动构造

        3.4当不接受返回值就没有办法优化

4.移动构造和移动赋值的默认生成条件(重载赋值的右值引用和移动构造跟拷贝构造差不多) 

5.万能引用 (模板参数引用&&)


1.右值和左值

左值与右值是C语言中的概念,但C标准并没有给出严格的区分方式,一般认为:可以放在=左边的,或者能 够取地址的称为左值,只能放在=右边的,或者不能取地址的称为右值,但是也不一定完全正确

左值可以取地址,可以赋值的

  • 下面的a,r1,p,r2就属于左值
  • b,r3虽然是被const修饰不能赋值,但是也是左值,左值是能取地址的值,可以被const修饰也是左值
	int a = 10;
	int& r1 = a;
	int* p = &a;
	int& r2 = *p;

	const int b = 10;
	const int& r3 = b;

右值:不能取地址的值

  • 纯右值,比如:X+Y, 10。
  • 将亡值,比如:表达式的中间结果((X+(Y+Z))Y+Z的值)、函数按照值的方式进行返回(返回值)。 
	//常见的右值
    10;
	x + y;
	fmin(x, y);

2.左值引用和右值引用 

  • 左值引用:b,d
  • 右值引用:a1,b1,c1

左值引用使用:&,右值引用使用:&&同时它们也是可以交叉引用

    //左值引用左值
    int a=1;
    int& b=a;

    int* c=&a;
    int& d=*c;
    //右值引用右值
    int X=10,int Y=20;

    int&& a1=10;
    int&& b1=X+Y;
    int&& c1=min(X,Y);

 2.1左右值的交叉引用

  • 左值引用右值-- 不能直接引用,但是const 左值引用可以引用右值

  • 右值引用左值-- 不能直接引用,但是可以右值引用可以引用move以后左值

    //左值引用右值-- 不能直接引用,但是const 左值引用可以引用右值
    const int& a=2;

    //右值引用左值-- 不能直接引用,但是可以右值引用可以引用move以后左值
    int a1=10;
    int&& b1=move(a1)

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第1张图片

2.2左值引用右值的具体情景

  1. 当参数直接传常数(右值),那么引用必须加一个const

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第2张图片

3.移动构造

class string
	{
	public:
        // 拷贝构造
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;

			string tmp(s._str);
			swap(tmp);
		}

		// 移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 资源转移" << endl;

		string operator+(char ch)
		{
			string tmp(*this);
			push_back(ch);

			return tmp;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};

3.1移动构造:资源转移,没有了深拷贝和delete效率大大提高

  1. 没有深拷贝了,效率提升;
  2. 将亡值资源被转移了,它被换成nullptr,调析构函数,没有资源需要析构效率提高 

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第3张图片

 3.2如果没有移动构造,调用重载+返回值是一个将亡值(右值),被s1接受,会调用两次拷贝构造(左值引用右值,但是编译器会有优化会当重载函数快结束的时候使用将亡值构造是s1;

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第4张图片

3.3如果有了移动构造,调用重载+返回值是一个将亡值(右值),被s1接受,会调用一次拷贝构造和一次移动构造,但是编译器会有优化会当重载函数快结束的时候使用将亡值移动构造是s1,资源转移比深拷贝效率更高

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第5张图片

 3.4当不接受返回值就没有办法优化

  • 不优化的过程:先构造临时对象,再用返回值(临时对象,右值)拷贝构造s1,所以两次深拷贝;编译器的优化,在被调用的函数栈帧快结束时,使用s1构造s2,所以一次深拷贝;

原因:如果不接受返回值和不优化的步骤不一样了,所以不能优化,只能深拷贝一次;

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第6张图片

 3.5push_back的右值引用的重载

  • 所有容器调用push_back都有左值引用和右值引用版本

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第7张图片

4.移动构造和移动赋值的默认生成条件(重载赋值的右值引用和移动构造跟拷贝构造差不多) 

  • 移动构造和移动赋值默认生成条件:当它本身被实现,且析构函数、拷贝构造、赋值运算符重载都没有实现才会默认生成;

5.万能引用 (模板参数引用&&)

  • 万能引用就是模板参数引用,使用的符号和右值引用相同:&&
  • 可以接受左值也可以接受右值
  • 接受后使用退化成了左值
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
void PerfectForward(T&& t)
{
	Fun(t);

	//Fun(std::forward(t));
}
int main()
{
	PerfectForward(10);           // 右值

	int a;
	PerfectForward(a);            // 左值
	PerfectForward(std::move(a)); // 右值

	const int b = 8;
	PerfectForward(b);		      // const 左值
	PerfectForward(std::move(b)); // const 右值

	return 0;
}

【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第8张图片

5.1完美转发 

完美转发是目标函数总希望将参数按照传递给转发函数(forward)实际类型转给目标函数,而不产生额外的开销;如果相应实参是左值,它就应该被转发为左值;如果相 应实参是右值,它就应该被转发为右值

格式:forward<模板参数>(实参)

使用完美转发结果:【C++右值引用】移动构造和移动赋值,万能引用,右值详讲_第9张图片

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