最近觉得应该多看看C++的新特性,否则别人代码中用到,自己不熟悉,看代码的效率就比较低。本周抽时间看了看移动语义,在这里简单介绍下:
背景
我们都知道深拷贝和浅拷贝,浅拷贝是两个变量指向同一块区域,这样删除对象的时候会删除内存同一块区域两次,有崩溃的风险;深拷贝是指原对象删除已有空间,然后重新开辟空间,并把参数对象的内容复制过来,两个对象两个空间,相对安全。但深拷贝临时对象会有一个效率问题,比如下面例子(加入深拷贝的参数是一个临时对象):
拷贝构造时,删除,创建,复制;然后临时对象会进行析构删除,进行4步操作;而下面的移动构造,只进行了删除和赋值,将自己的str指向临时对象中str的空间,并把临时对象的str置空(因为临时对象很快被删除,并且delete NULL是被允许的,所以是安全的)。这样的话下面的效率明显高于上面,移动赋值同理。这种只修改引用的指向,而实际数据还在原有地方的方法称为移动语义。
拷贝构造:是开辟新的空间,并进行复制,内存中两份数据。
移动构造:指向原有的空间,并把临时对象指针赋空,内存中一份数据。
要实现移动语义,就要让编译器知道什么时候需要复制,什么时候不需要。以前引用参数为const T&或T&,前者不能修改对象的指针,后者不允许匹配临时对象,所以我们需要一种类型来区别临时对象和普通对象,C++11提供了右值引用T&&进行判断,临时对象是一个右值,使用T&&可以匹配到移动构造和移动复制,这是右值引用的作用之一。
移动构造(移动赋值)解析
虽然使用右值引用可以支持移动语义,但这并不会神奇的发生。要让移动语义发生,需要两个步骤:
- 让编译器知道何时使用移动语义。即使用右值引用让编译器区分左值和右值。
- 编写移动构造函数,使其提供所需的行为。即改变引用的指向。
强制移动
ps:std::move()只是将左值转换成右值引用的类型,本身不会销毁空间,只有在定义了移动构造和移动赋值,才会进行配合执行(否则会执行复制拷贝和复制赋值)。
注意:
1. 右值引用无法绑定左值,但可以通过std::move实现
2. 左值引用无法绑定右值,但const左值引用可以绑定右值。