移動構造和移動賦值與std::move

來源:互聯網
上載者:User

標籤:匿名   臨時對象   地址   size   mil   null   不難   sap   標準庫   

 

 

---------------------------------------移動構造--------------------------------------------

傳統的深拷貝深賦值

  對於類中,含有指標的情況,要自實現其拷貝構造和拷貝賦值。也就是所謂的深拷貝和深賦值。我想這己經成為一種共識了。

比如如下類: 

#include <iostream>using namespace std;class HasPtrMem{ public:  HasPtrMem():_d(new int(0)){    cout<<"HasPtrMem()"<<this<<endl;  } 

HasPtrMem(const HasPtrMem& another):_d(new int(*another._d))
{  cout<<"HasPtrMem(const HasPtrMem&  another)"<<this<<"->"<<&another<<endl;}

~HasPtrMem(){  delete _d;  cout<<"~HasPtrMem()"<<this<<endl;  }

  int * _d;};HasPtrMem getTemp(){  return HasPtrMem();}int main(int argc, char *argv[]){

// HasPtrMem a;// HasPtrMem b(a);// cout<<*a._d<<endl;// cout<<*b._d<<endl;  HasPtrMem&& ret = getTemp();  return 0;}

 

 

  上面的過程,我們己經知曉,ret 作為右值引用,引用了臨時對象,由於臨時對象是待返回對象的複本,所以表面上看起來是,待返回對象的範圍擴充了,生命週期也延長了。

 

從右值引到移動構造


  前面我們建立起來了一個概念,就是右值引用。用右值引用的思想,再來實現一下拷貝。這樣,順便把臨時對象的問題也解決了。 

#include <iostream>using namespace std;class HasPtrMem{ public:    HasPtrMem():_d(new int(0)){    cout<<"HasPtrMem()"<<this<<endl;} HasPtrMem(const HasPtrMem& another):_d(new int(*another._d)){    cout<<"HasPtrMem(const HasPtrMem& another)" <<this<<"->"<<   &another<<endl;} 
HasPtrMem(HasPtrMem &&another){ cout<<this<<" Move resourse from "<<&another<<"->"<< another._d <<endl; _d = another._d; another._d = nullptr;} ~HasPtrMem(){ delete _d; cout<<"~HasPtrMem()"<<this<<endl; } int * _d;};HasPtrMem getTemp(){ return HasPtrMem();} int main(int argc, char *argv[]){ HasPtrMem a = getTemp(); return 0;}

 

移動構造

  如下是,移動建構函式。我們借用臨時變數,將待返回對象的內容“偷”了過來。 

  移動構造充分體現了右值引用的設計思想,通過移動構造我們也在對象層面看清了右值引用的本質。從而對於普通類型右值引用內部是怎樣操作的的也就不難理解了。

//移動構造
HasPtrMem(HasPtrMem &&another){ cout<<this<<" Move resourse from "<<&another<<"->"<< another._d<<endl; _d = another._d; another._d = nullptr;}

   再來看一下拷貝建構函式,我們對比一下區別:

HasPtrMem(const HasPtrMem& another):_d(new int(*another._d)){    cout<<"HasPtrMem(const HasPtrMem& another)" <<this<<"->"<<   &another<<endl;} 

 

  移動構造相比於拷貝構造的區別,移動構造通過指標的賦值,在臨時對象析構之前,及時的接管了臨時對象在堆上的空間地址。

 

 

 

關於預設的移動建構函式

  對於不含有資源的對象來說,自實現拷貝與移動語意並沒有意義,對於這樣的類型 而言移動就是拷貝,拷貝就是移動。 

  拷貝構造/賦值和移動構造/賦值,必須同時提供或是同時不提供。才能保證同時俱有拷貝和移動語意。只聲明一種的話,類只能實現一種語義。

  只有拷貝語義的類,也就是 C++98 中的類。而只有移動語意的類,表明該類的變數所擁有的資源只能被移動,而不能被拷貝。那麼這樣的資源必須是唯一的。只有移動語意構造的類型往往是“資源型”的類型。比如智能指標,檔案流等。

 

 效率問題

 

 

#include <iostream>

using namesapce std;


class Copyable{public: Copyable(int i) :_i(new int(i)) { cout<<"Copyable(int i):"<<this<<endl; } Copyable(const Copyable & another) :_i(new int(*another._i)) { cout<<"Copyable(const Copyable & another):"<<this<<endl; } Copyable(Copyable && another) { cout<<"Copyable(Copyable && another):"<<this<<endl; _i = another._i; } Copyable & operator=(const Copyable &another) { cout<<"Copyable & operator=(const Copyable &another):"<<this<<endl; if(this == & another) return *this; *_i=*another._i; return *this; } Copyable & operator=(Copyable && another) { cout<<"Moveable & operator=(Moveable && another):"<<this<<endl; if(this != &another) { *_i = *another._i; another._i = NULL; } return * this; } ~Copyable() { cout<<"~Copyable():"<<this<<endl; if(_i) delete _i; } void dis() { cout<<"class Copyable is called"<<endl; } void dis() const { cout<<"const class Copyable is called"<<endl; }private: int * _i;};void putRRValue(Copyable && a){ cout<<"putRRValue(Copyable && a)"<<endl; a.dis();}void putCLValue(const Copyable & a){ cout<<"putCRValue(Copyable & a)"<<endl; a.dis();//error!}//const T&和T&&重載同時存在先調用誰?void whichCall(const Copyable & a){ a.dis();}void whichCall(Copyable && a){ a.dis();}int main(int argc, char *argv[]){// Copyable rrc = getCopyable(); cout<<"調用移動構造"<<endl; Copyable a =Copyable(2);//匿名對象/臨時對象優先調用右值引用 構造-右值構造 cout<<"調拷貝構造"<<endl; Copyable ca(a); cout<<"直接構造右值"<<endl; Copyable && rra =Copyable(2); cout<<"=================================="<<endl; //右值引用與const引用。 效率是否一樣? cout<<"右值引用傳參"<<endl; putRRValue(Copyable(2)); cout<<"Const 引用傳參"<<endl; putCLValue(Copyable(2)); cout<<"----------------------"<<endl; //優先調用哪種重載? T&& 還是 const T&? whichCall(Copyable(2)); //這個沒什麼好糾結的!T&&的出現就是瞭解決 const T &接受匿名/臨時對象後,不能調用非cosnt函數的問題。 return 0;}

 

 

 

-----------------------------------------模板函數std::move----------------------------------------

 

  雖然不能將一個右值引用直接綁定到左值上,但是我們可以顯式的將一個左值轉換為對應的右值參考型別。我們還可以通過調用一個名為move的新標準庫函數來獲得綁定到左值上的右值引用,此函數定義在untility中。move函數使用了**機制來返回給定對象的右值引用。

 

int &&rra = rr1; //error:  //! error!右值引用不能直接綁定到左值int &&rr = std::move(rr1); // ok!

 

  move調用告訴編譯器:我們有一個左值,但是我們希望像處理一個右值一樣去處理他。

 

  調用move就意味著承諾:除了對rr1賦值或者銷毀它之外,我們將不能再使用它。在調用move之後,我們不能對移動後的來源物件值做任何的假設。

 

移動構造和移動賦值與std::move

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.