右值引用和移动语义

目录

引言

1, 左值引用和右值引用

2,左值引用与右值引用比较

3 右值引用使用场景和意义

左值引用和右值引用的核心作用

移动语义:移动拷贝和移动赋值

革命尚未成功,同志仍需努力


引言

上一期咱们介绍了C++11更新的一些内容,这一期咱们来继续学习C++11中一个非常重要的部分,右值引用和移动语义。

1, 左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们 之前学习的引用就叫做左值引用。

无论左值引用还是右值引用,都是给对象取别名。

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

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋 值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

简单的理解,可以取地址的表达式(如变量名或解引用的指针)就是左值。

什么是右值?什么是右值引用?

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引 用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能 取地址。右值引用就是对右值的引用,给右值取别名。

简单的理解,不能取地址的 数据的表达式就是右值

2,左值引用与右值引用比较

左值引用总结:

        1. 左值引用只能引用左值,不能引用右值。

        2. 但是const左值引用既可引用左值,也可引用右值。

右值引用总结:

1. 右值引用只能右值,不能引用左值。

2. 但是右值引用可以move以后的左值。

3 右值引用使用场景和意义

先看以下代码

namespace ssy
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			//cout << "string(char* str)" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// s1.swap(s2)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		// 移动构造
		string(string && s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动语义" << endl;
			swap(s);
		}
		// 移动赋值
		string& operator=(string && s)
		{
			cout << "string& operator=(string&& s) -- 移动语义" << endl;
			swap(s);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		//string operator+=(char ch)
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}

左值引用和右值引用的核心作用

左值引用的核心价值是减少拷贝,提高效率

右值引用的核心价值是进一步减少拷贝,弥补左值引用没有解决的场景,eg :传值返回

移动语义:移动拷贝和移动赋值

我们看下面代码和运行结果 (这是把移动构造和移动赋值屏蔽的情况)

那我们把移动构造和移动赋值加上

这是为什么呢? 

内置类型的右值是纯右值,但是自定义类型的右值是将亡值

自定义类型的右值都是将亡值。

将亡值的生命周期非常短,当他生命周期结束后,它的资源都要被回收,既然他的资源要被回收了,即没有用了,那可以我们把这个对于 将亡值 没有用的资源转移给需要资源的变量,这样及不会干扰程序正常运行,也可以对资源进行回收利用,减少拷贝。

具体怎么转移,就是用swap 进行交换

右值引用和移动语义解决上述问题:

在ssy::string中增加移动构造,移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不 用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

回到上述问题

func 函数 返回 s1 ,返回的是 s1 的临时变量,这个临时变量为右值,将临时变量赋值给s1 走的就是移动赋值。

那为什么没有看见 s1 深拷贝到临时变量呢?

编译器为了保证效率,会直接把 s1 识别成右值

革命尚未成功,同志仍需努力

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值