首页 > 软件开发 > C++ > 正文
2.2 move语义
2015-07-07 15:24:48     我来说两句      
收藏    我要投稿

我们知道移动语义是通过右值引用来匹配临时值的,那么,普通的左值是否也能借助移动语义来优化性能呢,那该怎么做呢?事实上C++11为了解决这个问题,提供了std::move方法来将左值转换为右值,从而方便应用移动语义。

move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。深拷贝和move的区别如图2-1所示。


 

在图2-1中,对象SourceObject中有一个Source资源对象,如果是深拷贝,要将SourceObject拷贝到DestObject对象中,需要将Source拷贝到DestObject中;如果是move语义,要将SourceObject移动到DestObject中,只需要将Source资源的控制权从SourceObject转移到DestObject中,无须拷贝。

move实际上并不能移动任何东西,它唯一的功能是将一个左值强制转换为一个右值引用,使我们可以通过右值引用使用该值,以用于移动语义。强制转换为右值的目的是为了方便实现移动构造。

这种move语义是很有用的,比如一个对象中有一些指针资源或者动态数组,在对象的赋值或者拷贝时就不需要拷贝这些资源了。在C++11之前拷贝构造函数和赋值函数可能要像下面这样定义。假设一个A对象内部有一个资源m_ptr:

A& A::operator=(const A& rhs)
{
// 销毁m_ptr指向的资源
// 复制rhs.m_ptr所指的资源,并使m_ptr指向它
}
同样A的拷贝构造函数也是这样。假设这样来使用A:
A foo(); // foo是一个返回值为X的函数
A a;
a = foo();

最后一行将会发生如下操作:

销毁a所持有的资源。

复制foo返回的临时对象所拥有的资源。

销毁临时对象,释放其资源。

上面的过程是可行的,但是更有效率的办法是直接交换a和临时对象中的资源指针,然后让临时对象的析构函数去销毁a原来拥有的资源。换句话说,当赋值操作符的右边是右值的时候,我们希望赋值操作符被定义成下面这样:

A& A::operator=(const A&& rhs)
{
         // 转移资源的控制权,无须复制
}

仅仅转移资源的所有者,将资源的拥有者改为被赋值者,这就是所谓的move语义。再看一个例子,假设一个临时容器很大,赋值给另一个容器。

{
   std::list< std::string> tokens; // 省略初始化……
   std::list< std::string> t = tokens;
}
std::list< std::string> tokens;
std::list< std::string> t = std::move(tokens);

如果不用std::move,拷贝的代价很大,性能较低。使用move几乎没有任何代价,只是转换了资源的所有权。实际上是将左值变成右值引用,然后应用move语义调用构造函数,就避免了拷贝,提高了程序性能。当一个对象内部有较大的堆内存或者动态数组时,很有必要写move语义的拷贝构造函数和赋值函数,避免无谓的深拷贝,以提高性能。事实上,C++中所有的容器都实现了move语义,方便我们实现性能优化。

这里也要注意对move语义的误解,move只是转移了资源的控制权,本质上是将左值强制转换为右值引用,以用于move语义,避免含有资源的对象发生无谓的拷贝。move对于拥有形如对内存、文件句柄等资源的成员的对象有效。如果是一些基本类型,比如int和char[10]数组等,如果使用move,仍然会发生拷贝(因为没有对应的移动构造函数),所以说move对于含资源的对象来说更有意义。

点击复制链接 与好友分享!回本站首页
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.1.2 右值引用优化性能,避免深拷贝
下一篇:2.3 forward和完美转发
相关文章
图文推荐
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站