深入理解Move Constructor和Move Assignment

    之前的一篇博客讲了左值引用和右值引用,并且细致地说到了右值实际上就是“一闪而过”的结果,它是程序在计算时候的一个中间量,这个中间量如果我们能够有效的利用上,那么我们再创建对象(Constructor)的时候或是给已有对象(Assignment)赋值的时候很多资源就不用再重新申请了,直接把右值的资源“steal”过来。不要小看这一步的改变,如果你的程序频繁地创建对象,并且一个对象创建需要进行申请很多内存资源和计算的话,那么这一小步就能够在效率上提高很多。基于以上的思想,C++11中引入Move Constructor和Move Assignment。下面我们写一段程序,理解一下这两个新伙伴。

class Object{
public:
    Object (string* resource) : _resource(resource){
        
    }
    Object (Object& object) : _resource(new string(*object._resource)){
        
    }
    Object (Object&& object) : _resource(object._resource){
        object._resource = nullptr;
    }
    Object& operator=(Object& object){
        if (&object != this){
            _resource = new string(*object._resource);
        }
        return *this;
    }
    Object& operator=(Object&& object){
        if (&object != this){
            if (_resource != nullptr){
                delete _resource;
            }
            _resource = object._resource;
            object._resource = nullptr;
        }
        return *this;
    }
    ~Object(){
        if (_resource != nullptr){
            delete _resource;
        }
    }
private:
    string* _resource = nullptr;
};

    可以看到在Object类中与以往相比有两个新成员函数:Object (Object&& object)和Object& opreator=(Object&& object),他们就是Move Constructor和Move Assignment。可以看到他们的特点是输入参数是右值引用,他们“steal”输入参数中的资源,节省资源提高效率,充分利用右值引用的特点,其实很好理解。

      但是如果想要深入理解Move Constructor和Move Assignment,需要注意的还有很多。

      1.众所周知,对于一个类,如果我们不定义它的Constructor、Copy Constructor、Copy Assignment和Destructor,编译器能够帮我们合成这些必备的函数。但是对于Move Constructor和Move Assignment,如果我们不定义他们,编译器也不会定义他们。但是有一个特例,就是如果一个类中如果他的成员变量都是可以“move”的(成员变量类自身定义了Move Constructor和Move Assignment),那么编译器会自动为这个类生成Move Constructor和Move Assignment。

struct X{
    int i;            //build-in类型是可以“move”的
    string s;         //string是可以“move”的
};                   //所以编译器能够为X合成Move Constructor和Move Assignment

struct Y{
    X x;            //X是可以“move”的
};                  //所以编译器能够为Y合成ove Constructor和Move Assignment        

       我们对于想要编译器合成的部分,可以使用using = default,但是对于Move Constructor和Move Assignment,仍然需要满足一定条件,否则即使使用using = default,最后的结果也不能够合成(using = delete)。a.在类中有成员变量不能够“move”的类;b.对于没有析构函数(desrucor = delete)的类(这种类能够被new出来,但是不能够被delete);c.有const或是引用的成员变量的类。

    2.对于一个没有Move Constructor和Move Assignment的类,当传入的参数是右值引用,那么右值引用会转换成const左值引用,然后调用该类的Copy Constructor和Copy Assignment。

     3.Copy-and-Swap Assignment Operator。这是一个有些trick的方式去定义Assignment Operator。还是上面的例子,我们可以这样定义Assignment Operator:

Object& operator=(Object object){
    swap(*this, object);
    return *this;
}

和以往不同的是这里的输入参数不是引用,而是一个实例,这也正是这么定义Assignment trick的地方。接着往下看:

object2 = object1                 //object1是左值,所以Copy Constructor将会被调用
object2 = std::move(object1)      //Move Constructor将会被调用

这下子一目了然了吧,一个Copy-and-Swap Assignment Operator可以替代Copy Assignment和Move Assignment。

    4.因为对于Copy Constructor和Copy Assignment涉及到内存的重新分配,所以在在Copy Constructor和Copy Assignment中不要抛出异常,因为一旦抛出异常,内存的分配状况不确定,从而出现不确定行为。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值