亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺析C++11中的右值引用、轉(zhuǎn)移語(yǔ)義和完美轉(zhuǎn)發(fā)

 更新時(shí)間:2016年08月26日 09:06:28   投稿:daisy  
對(duì)于c++11來(lái)說移動(dòng)語(yǔ)義是一個(gè)重要的概念,一直以來(lái)我對(duì)這個(gè)概念都似懂非懂。最近翻翻資料感覺突然開竅,因此順便記錄下C++11中的右值引用、轉(zhuǎn)移語(yǔ)義和完美轉(zhuǎn)發(fā),方便大家查閱參考。

1. 左值與右值:

    C++對(duì)于左值和右值沒有標(biāo)準(zhǔn)定義,但是有一個(gè)被廣泛認(rèn)同的說法:可以取地址的,有名字的,非臨時(shí)的就是左值;不能取地址的,沒有名字的,臨時(shí)的就是右值.

    可見立即數(shù),函數(shù)返回的值等都是右值;而非匿名對(duì)象(包括變量),函數(shù)返回的引用,const對(duì)象等都是左值.

    從本質(zhì)上理解,創(chuàng)建和銷毀由編譯器幕后控制的,程序員只能確保在本行代碼有效的,就是右值(包括立即數(shù));而用戶創(chuàng)建的,通過作用域規(guī)則可知其生存期的,就是左值(包括函數(shù)返回的局部變量的引用以及const對(duì)象),例如:

int& foo(){int tmp; return tmp;}

int fooo(){int tmp; return tmp;}

int a=10;

const int b;

int& temp=foo();//雖然合法,但temp引用了一個(gè)已經(jīng)不存在的對(duì)象

int tempp=fooo();

以上代碼中,a,temp和foo()都是非常量左值,b是常量左值,fooo()是非常量右值,10是常量右值,有一點(diǎn)要特別注意:返回的引用是左值(可以取地址)!

一般來(lái)說,編譯器是不允許對(duì)右值進(jìn)行更改的(因?yàn)橛抑档纳嫫诓挥沙绦騿T掌握,即使更改了右值也未必可以用),對(duì)于內(nèi)置類型對(duì)象尤其如此,但C++允許使用右值對(duì)象調(diào)用成員函數(shù),雖然允許這樣做,但出于同樣原因,最好不要這么做.

2. 右值引用:

    右值引用的表示方法為

 Datatype&& variable

    右值引用是C++ 11新增的特性,所以C++ 98的引用為左值引用.右值引用用來(lái)綁定到右值,綁定到右值以后本來(lái)會(huì)被銷毀的右值的生存期會(huì)延長(zhǎng)至與綁定到它的右值引用的生存期,右值引用的存在并不是為了取代左值引用,而是充分利用右值(特別是臨時(shí)對(duì)象)的建構(gòu)來(lái)減少對(duì)象建構(gòu)和析構(gòu)操作以達(dá)到提高效率的目的,例如對(duì)于以下函數(shù):

(Demo是一個(gè)類)
Demo foo(){ 
  Demo tmp;
  return tmp;
}

在編譯器不進(jìn)行RVO(return value optimization)優(yōu)化的前提下以下操作:

Demo x=foo();

將會(huì)調(diào)用三次構(gòu)造函數(shù)(tmp的,x的,臨時(shí)對(duì)象的),相應(yīng)的在對(duì)象被銷毀時(shí)也會(huì)調(diào)用三次析構(gòu)函數(shù),而如果采用右值引用的方式:

Demo&& x=foo();

那么就不需要進(jìn)行x的建構(gòu),本來(lái)本來(lái)要被銷毀的臨時(shí)對(duì)象也會(huì)由于x的綁定而將生存期延長(zhǎng)至和x一樣(可以理解為x賦予了那個(gè)臨時(shí)對(duì)象一個(gè)合法地位:一個(gè)名字),就需要提高了效率(代價(jià)就是tmp需要占據(jù)4字節(jié)空間,但這是微不足道的).

    右值引用與左值引用綁定規(guī)則:

         常量左值引用可以綁定到常量和非常量左值,常量和非常量右值;

         非常量左值引用只能綁定到非常量左值;

         非常量右值引用只能綁定到非常量右值(vs2013也可以綁定到常量右值);

         常量右值引用只能綁定到常量和非常量右值(非常量右值引用只是為了語(yǔ)義的完整而存在,常量左值引用就可以實(shí)現(xiàn)它的作用).

         雖然從綁定規(guī)則中可以看出常量左值引用也可以綁定到右值,但顯然不可以改變右值的值,右值引用就可以,從而實(shí)現(xiàn)轉(zhuǎn)移語(yǔ)義,因?yàn)橛抑狄猛ǔR淖兯壎ǖ挠抑?所以被綁定的右值不能為const.

    注意:右值引用是左值!

3. 轉(zhuǎn)移語(yǔ)義(move semantics):

    右值引用被引入的目的之一就是實(shí)現(xiàn)轉(zhuǎn)移語(yǔ)義,轉(zhuǎn)移語(yǔ)義可以將資源 ( 堆,系統(tǒng)對(duì)象等 ) 的所有權(quán)從一個(gè)對(duì)象(通常是匿名的臨時(shí)對(duì)象)轉(zhuǎn)移到另一個(gè)對(duì)象,從而減少對(duì)象構(gòu)建及銷毀操作,提高程序效率(這在2的例子中已經(jīng)作了解釋).轉(zhuǎn)移語(yǔ)義與拷貝語(yǔ)義是相對(duì)的.從轉(zhuǎn)移語(yǔ)義可以看出,實(shí)際上,轉(zhuǎn)移語(yǔ)義并不是新的概念,它實(shí)際上已經(jīng)在C++98/03的語(yǔ)言和庫(kù)中被使用了,比如在某些情況下拷貝構(gòu)造函數(shù)的省略(copy constructor elision in some contexts),智能指針的拷貝(auto_ptr “copy”),鏈表拼接(list::splice)和容器內(nèi)的置換(swap on containers)等,只是還沒有統(tǒng)一的語(yǔ)法和語(yǔ)義支持

    雖然普通的函數(shù)和操作符也可以利用右值引用實(shí)現(xiàn)轉(zhuǎn)移語(yǔ)義(如2中的例子),但轉(zhuǎn)移語(yǔ)義通常是通過轉(zhuǎn)移構(gòu)造函數(shù)和轉(zhuǎn)移賦值操作符實(shí)現(xiàn)的.轉(zhuǎn)移構(gòu)造函數(shù)的原型為Classname(Typename&&) ,而拷貝構(gòu)造函數(shù)的原型為Classname(const Typename&) ,轉(zhuǎn)移構(gòu)造函數(shù)不會(huì)被編譯器自動(dòng)生成,需要自己定義,只定義轉(zhuǎn)移構(gòu)造函數(shù)也不影響編譯器生成拷貝構(gòu)造函數(shù),如果傳遞的參數(shù)是左值,就調(diào)用拷貝構(gòu)造函數(shù),反之,就調(diào)用轉(zhuǎn)移構(gòu)造函數(shù).

例如:

class Demo{

public:

  Demo():p(new int[10000]{};

  Demo(Demo&& lre):arr(lre.arr),size(lra.size){lre.arr=NULL;}//轉(zhuǎn)移構(gòu)造函數(shù)

  Demo(const Demo& lre):arr(new int[10000]),size(arr.size){

    for(int cou=0;cou<10000;++cou)

      arr[cou]=lew.arr[cou];

  }

private:

  int size;

  int* arr;

}

    從以上代碼可以看出,拷貝構(gòu)造函數(shù)在堆中重新開辟了一個(gè)大小為10000的int型數(shù)組,然后每個(gè)元素分別拷貝,而轉(zhuǎn)移構(gòu)造函數(shù)則是直接接管參數(shù)的指針?biāo)赶虻馁Y源,效率搞下立判!需要注意的是轉(zhuǎn)移構(gòu)造函數(shù)實(shí)參必須是右值,一般是臨時(shí)對(duì)象,如函數(shù)的返回值等,對(duì)于此類臨時(shí)對(duì)象一般在當(dāng)行代碼之后就被銷毀,而采用轉(zhuǎn)移構(gòu)造函數(shù)可以延長(zhǎng)其生命期,可謂是物盡其用,同時(shí)有避免了重新開辟數(shù)組.對(duì)于上述代碼中的轉(zhuǎn)移構(gòu)造函數(shù),有必要詳細(xì)分析一下:

Demo(Demo&& lre):arr(lre.arr),size(lre.size)({lre.arr=NULL;}

lre是一個(gè)右值引用,通過它間接訪問實(shí)參(臨時(shí)對(duì)象)的資源來(lái)完成資源轉(zhuǎn)移,lre綁定的對(duì)象(必須)是右值,但lre本身是左值;

因?yàn)?code>lre是函數(shù)的局部對(duì)象,”lre.arr=NULL"必不可少,否則函數(shù)結(jié)尾調(diào)用析構(gòu)函數(shù)銷毀lre時(shí)仍然會(huì)將資源釋放,轉(zhuǎn)移的資源還是被系統(tǒng)收回.

4. move()函數(shù)

    3中的例子并非萬(wàn)能,Demo(Demo&& lre)的實(shí)參必須是右值,有時(shí)候一個(gè)左值即將到達(dá)生存期,但是仍然想要使用轉(zhuǎn)移語(yǔ)義接管它的資源,這時(shí)就需要move函數(shù).

    std::move函數(shù)定義在標(biāo)準(zhǔn)庫(kù)<utility>中,它的作用是將左值強(qiáng)行轉(zhuǎn)化為右值使用,從實(shí)現(xiàn)上講,std:move等同于static_cast<T&&>(lvalue) ,由此看出,被轉(zhuǎn)化的左值本身的生存期和左值屬性并沒有被改變,這類似于const_cast函數(shù).因此被move的實(shí)參應(yīng)該是即將到達(dá)生存期的左值,否則的話可能起到反面效果.

5. 完美轉(zhuǎn)發(fā)(perfect forwarding)

    完美轉(zhuǎn)發(fā)指的是將一組實(shí)參"完美"地傳遞給形參,完美指的是參數(shù)的const屬性與左右值屬性不變,例如在進(jìn)行函數(shù)包裝的時(shí)候,func函數(shù)存在下列重載:

void func(const int);
void func(int);
void func(int&&);

如果要將它們包裝到一個(gè)函數(shù)cover內(nèi),以實(shí)現(xiàn):

void cover(typename para){
  func(para);
}

使得針對(duì)不同實(shí)參能在cover內(nèi)調(diào)用相應(yīng)類型的函數(shù),似乎只能通過對(duì)cover進(jìn)行函數(shù)重載,這使代碼變得冗繁,另一種方法就是使用函數(shù)模板,但在C++ 11之前,實(shí)現(xiàn)該功能的函數(shù)模板只能采用值傳遞,如下:

template<typename T>
void cover(T para){
  ...
  func(para);
  ...
}

但如果傳遞的是一個(gè)相當(dāng)大的對(duì)象,又會(huì)造成效率問題,要通過引用傳遞實(shí)現(xiàn)形參與實(shí)參的完美匹配(包裹const屬性與左右值屬性的完美匹配),就要使用C++ 11 新引入的引用折疊規(guī)則:

函數(shù)形參       T的類型         推導(dǎo)后的函數(shù)形參

T&               A&                A&
T&               A&&              A&
T&&             A&                A&
T&&             A&&              A&&

 因此,對(duì)于前例的函數(shù)包裝要求,采用以下模板就可以解決:

template<typename T>
void cover(T&& para){
  ...
  func(static_cast<T &&>(para));
  ...
}

 

如果傳入的是左值引用,轉(zhuǎn)發(fā)函數(shù)將被實(shí)例化為:

void func(T& && para){

  func(static_cast<T& &&>(para));

}

應(yīng)用引用折疊,就為:

void func(T& para){

  func(static_cast<T&>(para));

}

如果傳入的是右值引用,轉(zhuǎn)發(fā)函數(shù)將被實(shí)例化為:

void func(T&& &&para){

   func(static_cast<T&& &&>(para));
}

應(yīng)用引用折疊,就是:

void func(T&& para){

  func(static_cast<T&&>(para));

}

對(duì)于以上的static_cast<T&&> ,實(shí)際上只在para被推導(dǎo)為右值引用的時(shí)候才發(fā)揮作用,由于para是左值(右值引用是左值),因此需要將它轉(zhuǎn)為右值后再傳入func內(nèi),C++ 11在<untility>定義了一個(gè)std::forward<T>函數(shù)來(lái)實(shí)現(xiàn)以上行為,

所以最終版本為

template<typename T>

void cover(T&& para){

  func(forward(forward<T>(para)));

}

std::forward的實(shí)現(xiàn)與static_cast<T&&>(para)稍有不同

std::forward函數(shù)的用法為forward<T>(para) , 若T為左值引用,para將被轉(zhuǎn)換為T類型的左值,否則para將被轉(zhuǎn)換為T類型右值

總結(jié)

以上就是關(guān)于C++11中右值引用、轉(zhuǎn)移語(yǔ)義和完美轉(zhuǎn)發(fā)的全部?jī)?nèi)容,這篇文章介紹的很詳細(xì),希望對(duì)大家的學(xué)習(xí)工作能有所幫助。

相關(guān)文章

  • C語(yǔ)言深入探索遞歸的特點(diǎn)

    C語(yǔ)言深入探索遞歸的特點(diǎn)

    程序調(diào)???的編程技巧稱為遞歸 recursion)函數(shù)??調(diào)???就是遞歸,你也可以理解成是?種嵌套結(jié)構(gòu),但遞歸分為倆部分,第?是“遞”,進(jìn)?嵌套結(jié)構(gòu)。第?是”歸“,最終會(huì)?步?步返回。第?次接觸遞歸都會(huì)很懵,慢慢理解這個(gè)過程就明?了
    2022-06-06
  • C語(yǔ)言深入探索浮點(diǎn)數(shù)的使用秘密

    C語(yǔ)言深入探索浮點(diǎn)數(shù)的使用秘密

    在C語(yǔ)言中,浮點(diǎn)數(shù)是一個(gè)很重要的類型,浮點(diǎn)數(shù)可以使數(shù)據(jù)更為精確。浮點(diǎn)數(shù)說白了就是帶有小數(shù)點(diǎn)的數(shù)。比如1.6?0.0000?765.2等等,浮點(diǎn)數(shù)具體是怎么用的呢,讓我們一起來(lái)看看
    2022-04-04
  • QT實(shí)現(xiàn)用戶登錄注冊(cè)功能

    QT實(shí)現(xiàn)用戶登錄注冊(cè)功能

    這篇文章主要為大家詳細(xì)介紹了QT實(shí)現(xiàn)用戶登錄注冊(cè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C語(yǔ)言構(gòu)建連連看游戲(矩陣方式)

    C語(yǔ)言構(gòu)建連連看游戲(矩陣方式)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言構(gòu)建連連看游戲,采用矩陣方式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C/C++?QT實(shí)現(xiàn)解析JSON文件的示例代碼

    C/C++?QT實(shí)現(xiàn)解析JSON文件的示例代碼

    JSON是一種輕量級(jí)的數(shù)據(jù)交換格式,它是基于ECMAScript的一個(gè)子集,使用完全獨(dú)立于編程語(yǔ)言的文本格式來(lái)存儲(chǔ)和表示數(shù)據(jù)。這篇文章主要介紹了QT實(shí)現(xiàn)解析JSON文件的示例代碼,需要的可以參考一下
    2022-01-01
  • C語(yǔ)言數(shù)組和指針,內(nèi)存之間的關(guān)系

    C語(yǔ)言數(shù)組和指針,內(nèi)存之間的關(guān)系

    這篇文章主要介紹了C語(yǔ)言數(shù)組和指針,內(nèi)存之間的關(guān)系,首先論證一維數(shù)組和一級(jí)指針之前的關(guān)系,我們常常使用一級(jí)指針指針的方式訪問一維數(shù)組,只有對(duì)內(nèi)存的理解到位才能理解它們直接的關(guān)系。需要的小伙伴可以參考一下
    2022-02-02
  • android studio創(chuàng)建C++項(xiàng)目的實(shí)現(xiàn)示例

    android studio創(chuàng)建C++項(xiàng)目的實(shí)現(xiàn)示例

    本文主要介紹了android studio創(chuàng)建C++項(xiàng)目的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • C++實(shí)現(xiàn)鄰接表頂點(diǎn)的刪除

    C++實(shí)現(xiàn)鄰接表頂點(diǎn)的刪除

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)鄰接表頂點(diǎn)的刪除,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C語(yǔ)言創(chuàng)建數(shù)組實(shí)現(xiàn)函數(shù)init,empty,reverse

    C語(yǔ)言創(chuàng)建數(shù)組實(shí)現(xiàn)函數(shù)init,empty,reverse

    這篇文章主要介紹了C語(yǔ)言創(chuàng)建數(shù)組實(shí)現(xiàn)函數(shù)init,empty,reverse,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    這篇文章主要介紹了StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08

最新評(píng)論