• 【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; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、左值引用的优缺:

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

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

    四、移动拷贝

    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;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

    这里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
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
  • 相关阅读:
    PostgreSQL的MVCC对比Oracle的MVCC有什么优劣势?
    JVM——5.类文件结构
    【Linux】进程控制 —— 进程替换
    【无App Push 通用测试方案
    PostgreSQL逻辑备份与恢复
    [Spring boot] Spring boot 实现发送邮件功能
    栈的实现-c语言实现
    FPGA硬件工程师Verilog面试题(三)
    JavaWeb中的VUE快速入门
    第一章: HTML
  • 原文地址:https://blog.csdn.net/m0_74774759/article/details/133363753