移动语义之我见

C++11中最晦涩难懂的特性大约就是移动语义了。我最早看到的移动语义是和右值引用一起说的,更有甚者几乎把两者混为一谈。我并不是一个学院派,不敢在此深究二者定义上的区别,但我以为右值引用只是触发移动语义的必要条件而已。
首先谈一谈为什么引入了移动语义,移动语义较以前的拷贝有什么优点?我想先用两个例子来说明这个问题:

class Echo {
public:
    Echo() { std::cout << "new" << std::endl; }
    Echo(const Echo&) { std::cout << "new" << std::endl; }
};

int main(int argc, char** argv) {
    std::vector<Echo> v1, v2;
    v1.resize(5);
    std::cout << v1.size() << " "
              << v2.size() << std::endl;
    v2 = v1;
    std::cout << v1.size() << " "
              << v2.size() << std::endl;
    return 0;
}

运行结果如下:

new
new
new
new
new
5 0
new
new
new
new
new
5 5

main函数修改为如下之后

int main(int argc, char** argv) {
    std::vector<Echo> v1, v2;
    v1.resize(5);
    std::cout << v1.size() << " "
              << v2.size() << std::endl;
    v2 = std::move(v1);
    std::cout << v1.size() << " "
              << v2.size() << std::endl;
    return 0;
}

输出结果为:

new
new
new
new
new
5 0
0 5

不难看出,一段相同功能的代码,使用移动语义大大减少了vector的拷贝次数,从而提升了代码效率。而第二份代码拷贝次数的减少是建立在v1被清空的基础上。所以移动语义的本质其实就是将动态分配的内存的所有权从一个对象转移到另一个对象身上,原来的对象在失去所有权时往往需要将指针设置为空指针,对应到vector对象就意味着size为0。这个过程和auto_ptr的赋值和拷贝几乎一模一样。
明白这上面这点后就很容易理解该在什么时候使用移动语义了。首先一个对象被移动后会失去对动态内存的所有权,一定程度上意味着这个对象不可再用了,因此匿名变量就满足条件,因为匿名变量就是在C++代码中看不到的变量,无法在以后访问它。常见的匿名变量如函数返回值,表达式的值等。或者程序员确定某个有名对象后面不会被使用或者对象会被重置,则可以使用std::move显式地将其转换为右值引用。
或许说到这里依然有很多人对移动语义的作用持怀疑态度,因为我们最一开始给出的例子或许并不常见,又或者很多人都已经习惯了函数参数传引用、返回引用的设计方法,觉得移动语义带来的改变时微乎其微的。那么,请看下面的例子:

class Echo {
public:
    Echo() { std::cout << "new" << std::endl; }
    Echo(const Echo&) { std::cout << "new" << std::endl; }
    Echo& operator=(const Echo&) { std::cout << "=" << std::endl; }
    Echo(Echo&&) { std::cout << "move" << std::endl; }
    Echo& operator=(Echo&&) { std::cout << "move" << std::endl; }
};

int main(int argc, char** argv) {
    Echo a,b;
    std::swap(a, b);
    return 0;
}

这段代码的运行结果为:

new
new
move
move
move

注意最上面的两次new是创建a,b对象打印出来的,这意味引入移动语义可以使swap的不再需要拷贝,而老的C++标准下则需要拷贝三次(因为swap还会用到一个临时变量呢)

注:上面所说的拷贝并不是指Echo对象本身的拷贝,而是指对象所持有的动态内存的拷贝。这里最后强调一遍,移动语义只有对直接或间接(基类或成员)有动态分配的内存的对象才有效果。如果以后代码中出现了类似本文Echo类的移动构造和赋值函数,那就说明还是没懂移动语义。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值