【C++11】左值引用右值引用,移动构造的使用

🌏博客主页: 主页
🔖系列专栏: C++
❤️感谢大家点赞👍收藏⭐评论✍️
😍期待与大家一起进步!



一、左值与右值

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

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

二、 引用总结:

1.左值:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值
 	int a = 10;
    int& ra1 = a;   // ra为a的别名
    //int& ra2 = 10;   // 编译失败,因为10是右值
    // const左值引用既可引用左值,也可引用右值。
    const int& ra3 = 10;
    const int& ra4 = a; 

2.右值:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。
// 右值引用只能右值,不能引用左值。
 int&& r1 = 10;
 
 // error C2440: “初始化”: 无法从“int”转换为“int &&”
 // message : 无法将左值绑定到右值引用
 int a = 10;
 int&& r2 = a;
 
 // 右值引用可以引用move以后的左值
 int&& r3 = std::move(a);

三、左值引用的优缺:

优点:做参数和返回值都可以提高效率。
缺点:当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。

但这里传值返回又有一个问题,那就是效率很低,而且我进行的都是深拷贝,根据编译器的不同,有可能会进行两次拷贝构造。
在这里插入图片描述
在这里插入图片描述

四、移动拷贝

1.铺垫知识

在讲解移动拷贝之前我们先来引入几个概念:
内置类型的右值:纯右值
自定义类型的右值:将亡值(因为我们自定义类型,那上面的s=func()来说,过了这一行后,我返回的func就要被销毁了,一般生命周期只有一行)

在const左值引用与右值引用都存在的情况下,我们给右值起别名,编译器会走更匹配的
在这里插入图片描述

2.原理:

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

string(string&& s)
			:_str(nullptr)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;
			swap(s);
			//因为这里我们用的右值引用,
			//我们传值返回的时候会将str识别为将亡值
			//因为它满足将亡值的特性,过了return以后就被销毁了
			//既然你反正都要销毁,不如和我交换一下
		}


string& operator=(string&& s)
		{
			cout << "string& operator=(string && s) -- 移动拷贝" << endl;
			swap(s);

			return *this;
		}

在这里插入图片描述
在这里插入图片描述

这里func函数返回的str,如果不做其他说明,编译器会把其强行当成右值,然后会去调用移动拷贝。

这里没有调用深拷贝的拷贝构造,而是调用了移动构造,移动构造中没有新开空间,拷贝数据,所以效率提高了。

五、其他场景

浅拷贝的类不需要实现移动构造,因为浅拷贝的拷贝构造消耗不是很大
右值引用的核心价值是进一步减少拷贝,解决左值引用没有解决的场景,如传值返回
在这里插入图片描述

、 源码

这里为了方便看,所以删除了一些函数,详细可以到【C++】string类的模拟实现(增删查改,比大小,运算符重载)

namespace bit
{
	class string
	{
	public:
	 
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			//cout << "string(const 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& operator=(string&& s)
		{
			cout << "string& operator=(string && s) -- 移动拷贝" << endl;
			swap(s);

			return *this;
		}

		 

		string( string&& s)
			:_str(nullptr)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;
			swap(s);
			//因为这里我们用的右值引用,
			//我们传值返回的时候会将str识别为将亡值
			//因为它满足将亡值的特性,过了return以后就被销毁了
			//既然你反正都要销毁,不如和我交换一下
		}

		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()
		{
			delete[] _str;
			_str = nullptr;
		}
  
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值