深入理解C++11(九)

深入理解C++11(九)

move语义
我们知道移动语义是通过右值引用来匹配临时值的,那么,普通的左值是否也能借助移动语义来优化性能呢,那该怎么做呢?事实上C++11为了解决这个问题,提供了std::move方法来将左值转换为右值,从而方便应用移动语义。move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。深拷贝和move的区别如图2-1所示。
在这里插入图片描述

在这里插入图片描述
copy和move区别

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

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

这种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对于含资源的对象来说更有意义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值