• c++ 移动语义 完美转发


    移动语义

    移动语义的使用场景

    在写代码的时候会出现低效拷贝。如在string s1 = get_str();中先构造了一个临时对象,然后将这个临时对象拷贝赋值给了s1。
    最高效的操作应该是s1直接表示get_str中构造的s内存。
    移动语义的实现就是为了减少拷贝。

    string get_str(){
        string s;
        ...
        return s;
    }
    int main(){
        string s1 = get_str();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    移动语义的使用

    实现移动语义,就是实现类的移动赋值函数移动拷贝函数

    class Array {
    	Array(int sz);
    
    	Array(const Array& other);//拷贝构造函数
    	Array& operator = (const Array & other); //拷贝赋值函数
    	
    	Array(const Array&& other);//移动构造函数
    	Array& operator = (const Array && other);//移动赋值函数
    
    	~Array(); //析构函数
    private:
    	size_t sz; //记录资源个数
    	int* m_data; //指向资源的指针
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在语法上,拷贝函数参数是左值引用,移动函数参数是右值引用
    在实现上,拷贝函数被拷贝的原对象不会有改变,自身的值变为拷贝对象的值;移动函数不保证原对象的值会变成什么样,只保证原对象的状态是安全的,可析构的,原对象的值“移动”给自身用。

    class Array {
    	...
    	//拷贝构造函数
    	Array(const Array& other) {
    		sz = other.sz;
    
    		m_data = new int[sz];
    		for (size_t i = 0; i < sz; i++)
    			...//拷贝数据
    	
    	}
    	//拷贝赋值函数	
    	Array& operator = (const Array& other){
    		sz = other.sz;
    
    		delete[] m_data;
    		m_data = new int[sz];
    		for (size_t i = 0; i < sz; i++)
    			...//拷贝数据
    	}
    	
    	//移动构造函数
    	Array(const Array&& other) {
    		sz = other.sz;
    
    		m_data = other.m_data;
    		other.m_data = nullptr;
    		other.sz = 0;
    	}
    	//移动赋值函数
    	Array& operator = (const Array&& other) {
    		sz = other.sz;
    
    		m_data = other.m_data;
    		other.m_data = nullptr;
    		other.sz = 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

    在使用上,左值引用只能绑定左值,右值引用只能绑定右值。(const左值引用特殊,可以绑定右值)。
    因此要声明一个右值引用应该使用move。
    move底层实现是使用static_cast完成了一个类型转换,并没有实际的移动的操作。

    int main() {
    	Array a1(10);
    	Array& lref = a1;
    	Array a3(a1); //拷贝初始化
    	Array a4(lref);//拷贝初始化
    
    	Array&& a4 = a1;//错误,右值引用不能绑定左值
    	Array&& a5 = move(a1);
    	Array a4(a5);//移动初始化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    PS:右值和左值的区别
    左值:有一定的生命周期
    右值:生命期一般不超过一句话,辅助表达式的执行(如临时对象)
    所以右值引用是左值。

    完美转发

    完美转发的使用场景

    在一些函数中,一些参数是需要转发给另外的函数的
    比如标准库revoke(f,args...)执行f(args...)
    比如vector的emplace_back(args...) ,会在vector的尾部利用args来构造一个类,需要调用构造函数。
    完美转发就是希望在传参时,传入的类型不变。如果args是左值引用的,那么传给构造函数的也应该是左值引用;如果args是右值引用的,那么传给构造函数的也应该是右值引用。

    完美转发的使用

    参数传入要写成万能引用,转发时调用forward

    template<typename Func,typename T>
    auto myInvoke(Func f,T&& arg){
    	return f(std::forward<T>(arg));
    }
    
    • 1
    • 2
    • 3
    • 4

    T&& arg是万能引用的写法,详见这个博客

    必看:
    大佬视频,深入又清晰

  • 相关阅读:
    SPOJ DQUERY D-query(主席树维护区间 求区间内不同数字的个数)
    网络攻防原理与技术 第一章 课后题
    华为开启2022全球校园AI算法精英大赛 百万奖金等你来挑战算法极限
    使用Nodejs搭建简单的Web网页并实现公网访问
    修改和完成SpringSecurity的登录功能
    android鼠标滚轮事件监听方法
    WebRTC[50] - WebRTC支持SVC时SDP信令的协商过程
    基于JAVA清颜广告股份有限公司网站演示录像计算机毕业设计源码+数据库+lw文档+系统+部署
    quarkus的异步操作,神奇
    python 小案例96
  • 原文地址:https://blog.csdn.net/qq_36183881/article/details/126700260