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

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


文章目录

  • 一、左值与右值
  • 二、 引用总结:
    • 1.左值:
    • 2.右值:
  • 三、左值引用的优缺:
  • 四、移动拷贝
    • 1.铺垫知识
    • 2.原理:
  • 五、其他场景
  • 、 源码


一、左值与右值

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时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);

三、左值引用的优缺:

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

但这里传值返回又有一个问题,那就是效率很低,而且我进行的都是深拷贝,根据编译器的不同,有可能会进行两次拷贝构造。
【C++11】左值引用右值引用,移动构造的使用_第1张图片
【C++11】左值引用右值引用,移动构造的使用_第2张图片

四、移动拷贝

1.铺垫知识

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

在const左值引用与右值引用都存在的情况下,我们给右值起别名,编译器会走更匹配的
【C++11】左值引用右值引用,移动构造的使用_第3张图片

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;
		}

【C++11】左值引用右值引用,移动构造的使用_第4张图片
【C++11】左值引用右值引用,移动构造的使用_第5张图片

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

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

五、其他场景

浅拷贝的类不需要实现移动构造,因为浅拷贝的拷贝构造消耗不是很大
右值引用的核心价值是进一步减少拷贝,解决左值引用没有解决的场景,如传值返回
【C++11】左值引用右值引用,移动构造的使用_第6张图片

、 源码

这里为了方便看,所以删除了一些函数,详细可以到【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
	};
}

你可能感兴趣的:(c++,java,jvm)