移动构造函数

  • Post author:
  • Post category:其他



移动构造:


移动构造是C++11标准中提供的一种新的构造方法。


在现实中有很多这样的例子,我们将钱从一个账号转移到另一个账号,将手机


SIM


卡转移到另一台手机,将文件从一个位置剪切到另一个位置


……


移动构造可以减少不必要的复制,带来性能上的提升。


有些复制构造是必要的,我们确实需要另外一个副本;而有些复制构造是不必要的,我们可能只是希望这个对象换个地方,移动一下而已。


在C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。






而现在


在某些情况下,我们没有

必要复制对象——只需要移动它们








C++11引入移动语义:


~源对象资源的控制权全部交给目标对象


对比一下复制构造和移动构造:


复制构造是这样的:


在对象被复制后临时对象和复制构造的对象各自占有不同的同样大小的堆内存,就是一个副本。




移动构造是这样的:


就是让这个临时对象它原本控制的内存的空间转移给构造出来的对象,这样就相当于把它移动过去了。







复制构造和移动构造的差别:


这种情况下,我们觉得这个临时对象完成了复制构造后,就不需要它了,我们就没有必要去首先产生一个副本,然后析构这个临时对象,这样费两遍事,又占用内存空间,所幸将临时对象它的原本的资源直接转给构造的对象即可了。


当临时对象在被复制后,就不再被利用了。我们完全可以把临时对象的资源直接移动,这样就避免了多余的复制构造。


什么时候该触发移动构造呢?


如果临时对象即将消亡,并且它里面的资源是需要被再利用的,这个时候我们就可以触发移动构造。





移动构造是需要通过移动构造函数来完成的。


移动构造函数定义形式:


class_name(class_name && )


例:

函数返回含有指针成员的对象


有两种版本:


版本一:使用深层复制构造函数


~ 返回时构造临时对象,动态分配临时对象返回到主调函数,然后删除临时对象。


版本二:使用移动构造函数


~ 将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过过程。





版本一举例:使用复制构造的代码:



  1. #include<iostream>





  2. using




    namespace


    std;



  3. class


    IntNum{



  4. public


    :


  5. IntNum(

    int


    x = 0):xptr(


    new




    int


    (x)){



    //构造函数




  6. cout<<

    “Calling constructor…”


    <<endl;


  7. }

  8. IntNum(

    const


    IntNum &n):xptr(


    new




    int


    (*n.xpr)){



    //复制构造函数




  9. cout<<

    “Calling copy constructor…”


    <<endl;


  10. }

  11. ~IntNum(){


    //析构函数





  12. delete


    xpr;


  13. cout<<

    “Destructing…”


    <<endl;


  14. }


  15. int


    getInt(){



    return


    *ptr;}


    //返回指针所指向的值,而不是返回指针本身





  16. private


    :



  17. int


    *ptr;


  18. };



  19. //返回值为IntNum类对象




  20. IntNum getNum(){


  21. //定义了一个局部对象,然后将局部对象作为结果返回




  22. IntNum a;


  23. //返回值是IntNum类型





  24. return


    a;


  25. }


  26. int


    main(){



  27. //getNum()函数返回了一个IntNum类型的对象(临时无名对象),之后调用类的函数




  28. cout<<

    “getNum().getInt()”


    <<endl;



  29. return


    0;


  30. }





版本一通过深层复制构造函数实现的返回含有指针成员的对象,在这个过程中,做了一些无用功,既需要构造临时无名(注:是无名对象,不是上例中的对象a)对象,还要对其进行析构。


在上例中不构造临时无名对象不行吗?其实呢我们也不用构造临时无名对象了,对象a


反正是要消亡的,我们把要消亡的对象a占用的资源转移给临时对象,不行吗?!

这样的方案就是使用移动构造


版本二:使用移动构造


使用移动构造函数的代码举例:




  1. #include<iostream>





  2. using




    namespace


    std;



  3. class


    IntNum{



  4. public


    :


  5. IntNum(

    int


    x = 0):xptr(


    new




    int


    (x)){



    //构造函数




  6. cout<<

    “Calling constructor…”


    <<endl;


  7. }

  8. IntNum(

    const


    IntNum &n):xptr(


    new




    int


    (*n.xpr)){



    //复制构造函数




  9. cout<<

    “Calling copy constructor…”


    <<endl;


  10. }

  11. IntNum(IntNum && n):xptr(n.xptr){


    //移动构造函数




  12. n.xptr = nullptr;

  13. cout<<

    “Calling move constructor…”


    <<endl;


  14. }

  15. ~IntNum(){


    //析构函数





  16. delete


    xpr;


  17. cout<<

    “Destructing…”


    <<endl;


  18. }


  19. int


    getInt(){



    return


    *ptr;}


    //返回指针所指向的值,而不是返回指针本身





  20. private


    :



  21. int


    *ptr;


  22. };



  23. //返回值为IntNum类对象




  24. IntNum getNum(){


  25. //定义了一个局部对象,然后将局部对象作为结果返回




  26. IntNum a;


  27. //返回值是IntNum类型





  28. return


    a;


  29. }


  30. int


    main(){



  31. //getNum()函数返回了一个IntNum类型的对象(临时无名对象),之后调用类的函数




  32. cout<<

    “getNum().getInt()”


    <<endl;



  33. return


    0;


  34. }

在该例中的移动构造函数

IntNum(IntNum && n):xptr(n.xptr){//移动构造函数


n.xptr = nullptr;


cout<<“Calling move constructor…”<<endl;


}

好像干了件很危险的事情:直接用参数对象(n)里面的指针(n.xptr)来初始化当前对象的指针(xptr),(按理说这不是浅层复制吗?!说了有指针成员,我们复制时不能做这种浅层复制的呀,怎么在这里我们恰恰做浅层复制了呢)

看函数体里面,我们发现再做完xptr(n.xptr)这种指针对指针的复制(也就是把参数指针所指向的对象转给了当前正在被构造的指针)后,接着就把参数n里面的指针置为空指针(n.xptr = nullptr;),对象里面的指针置为空指针后,将来析构函数析构该指针(delete xpr;)时,是delete一个空指针,不发生任何事情,这就是一个移动构造函数。

移动构造函数中的参数类型,&&符号表示是右值引用;即将消亡的值就是右值,函数返回的临时变量也是右值,这样的单个的这样的引用可以绑定到左值的,而这个引用它可以绑定到即将消亡的对象,绑定到右值


左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值指表达式结束时就不再存在的临时对象——显然右值不可以被取地址。


关于左值、右值和右值引用的详细内容可以参考:

http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/