C++ 移动语义

  在谈论移动语义之前,我们先来了解一下什么是左值和右值。

左值和右值

c++中,左值代表一个在内存中有确定位置的对象(有地址)。而右值不在内存中有确定位置,比如临时变量、表达式、匿名变量和lambda表达式等。(左值和已被修改 而右值不能)左值是持续的;右值是短暂的,并且右值不会将原对象置于有效状态。
PS:一个变量的生命周期在超出作用域后结束。如果一个变量代表一个对象,当然这个对象的生命周期也在那时结束。对于临时对象也是如此,C++ 的规则是:一个临时对象会在包含这个临时对象的完整表达式估值完成后、按生成顺序的逆序被销毁,除非有生命周期延长发生。然而我们可以通过右值引用来延长临时对象的声明周期。

T&& k = getvalue();    // 右值引用 右值为匿名对象
T&& k  //  右值引用类型的变量可能是左值或右值,取决于它的初始化

这里getvalue产生的临时值不会在表达式结束后被销毁,它的生命周期因为右值引用得以延长。通过这个特点避免临时对象的拷贝和析构。

为什么要使用移动操作

一般来说,拷贝一个资源会导致一些额外的开销(如果一个对象占用的堆内存很大)。在这种拷贝没有必要的情况下,使用移动操作可以提升性能、减少开销。例如一个堆内存的类必须提供一个深拷贝构造函数,因为默认构造函数是浅拷贝,会产生指针悬挂问题;但是深拷贝函数也会造成额外的性能损耗。(比如一个函数返回一个临时变量,通过这个临时变量构造新的对象,临时变量在拷贝构造完成后就销毁了,如果堆内存很大那么拷贝的代价也很大),因此我们引入了移动构造函数,它的参数是右值引用类型,对于临时值只需要做浅拷贝即可,移动构造函数只是将资源的所有者转移,避免了临时变量的深拷贝问题(减少一次拷贝的过程),这也就是移动语义(右值引用的重要作用)。这个图可能便于理解。
在这里插入图片描述

简单总结一下就是移动语义使得在 C++ 里返回大对象(如容器)的函数和运算符成为现实,因而可以提高代码的简洁性和可读性,提高程序员的生产率。

如何实现移动操作

  • 为对象定义分开的拷贝构造函数和移动构造函数。
  • 接管给定对象的内存。其实也就是定义一个swap交换函数,和另一个对象快速交换成员。
  • 在接管内存之后,将给定对象中的指针置为nullptr。
  • 最后呢由于移动操作是“窃取”资源所有权,通常不分配任何资源,因此移动操作通常不会抛出异常,注意应当标为noexcept。
  • 移动操作是通过右值引用来匹配临时值,普通的左值也可以转换为右值(通过 std::move()函数实现 头文件为utility)。move是将对象的所有权从一个对象转移到另一个对象,不涉及内存拷贝。使用move几乎没有任何代价,只是转换了资源的所有权。他实际上将左值变成右值引用,然后应用移动语义,调用移动构造函数,避免了拷贝,提高了程序性能。如下即为简单的移动构造函数实现:
My_String::My_String(My_String&& str)noexcept {
	cout << "My_String move" << endl;
	if (str.m_data != this->m_data) {
		this->m_data = str.m_data;
		str.m_data = nullptr;
		this->m_size = str.m_size;
	}
	cout << this->m_data << endl;
}

相关注意事项

  • 定义了一个移动构造函数或移动赋值运算符的类也必须定义自己的拷贝操作,否则这些成员默认被定义为删除的(即无法使用)。----C++ primer 5th P477 其实和我们如果没定义拷贝构造函数,编译器会为我们生成合成构造函数一样,编译器也会生成和成移动构造函数,只是合成条件不同,这块读者可自行查看C++ primer 5th P475 水平有限,我理解的也不是很好哈哈。
  • 一般来说也可以通过move函数来移动内置类型,但是它其实是调用拷贝构造函数。即如果一个类型一了拷贝构造函数但是未定义移动构造,即使遇到右值也会调用拷贝构造函数,其对象是通过拷贝构造函数来“移动”。

关于引用坍塌和完美转发的些许理解

对于 template foo(T&&) 这样的代码:

  • 如果传递过去的参数是左值,T 的推导结果是左值引用;如果传递过去的参数是右值,T 的推导结果是参数的类型本身。
  • 如果 T 是左值引用,那 T&& 的结果仍然是左值引用——即 type& && 坍缩成了 type&。
  • 如果 T 是一个实际类型,那 T&& 的结果自然就是一个右值引用。

对于完美转发我的理解是:通过引用坍塌在接受一个参数时能适应所有情况,包括左值和右值。可以按照模板参数类型进行转发,如果参数是右值,自动匹配移动构造;如果是左值,自动匹配拷贝构造。这里要引入std::forward()函数恢复模板参数类型变化实现,读者可自行了解。

因为水平有限,这一章节理解的不是很深入,若有错误恳请批评指正。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值