C++ 左引用和右引用
說明1:
首先我們區(qū)分什么是左值和右值,有一個很好區(qū)分左值和右值的方式,就是是否可以對表達(dá)式取地址?。】梢垣@取地址的表達(dá)式就是左值,且持久性變量都是左值,反之則是右值。左值引用和右值引用都是對象的別名而已,左值引用就是左值的別名,右值引用就是右值的別名。所以左值引用只能綁定左值,右值引用只能綁定右值,我們不能將一個右值引用類型變量綁定到一個右值引用。因為變量都是左值!我們可以對右值引用類型變量取地址!但是有一個例外,const左值引用可以綁定到右值。這看起來好像有點難以理解,我們用代碼實際解釋一下。
int i = 42; //i是左值,可以對i取地址
int &r = i; //r是左值引用,綁定左值i
右值引用無法和右值引用類型變量綁定,因為右值引用類型變量是一個左值,所有持久性變量都是左值。可以取地址的變量是持久性變量,反之是臨時性變量。
說明2:
我們可以將左值引用看為一個對象的別名。左值引用的聲明包含一個可選的說明符列表,后面跟一個引用聲明符。左值引用必須被初始化,且不能再指向另外一個對象或設(shè)置為null。:
在C++11中,引入了右值引入,可以通過一個可變引用指向rvalue,但是不能綁定到lvalue,因此右值引用可以檢測一個值是否是臨時對象。 類似于左值引用使用&,右值引用使用&&,可以是const/non-const
說明3:
在 C++ 中,引用類型是一種復(fù)合類型,可以分為左值引用、常引用和右值引用
在了解什么是左值引用和右值引用之前,我們需要先了解什么是左值和右值。由于 C++ 本身沒有給出左值和右值的標(biāo)準(zhǔn)定義,
可以取地址的,有名稱的,非臨時的是左值
不可取地址,匿名的,臨時的是右值
左值引用
左值引用是我們平時在編程時最常使用到的。引用的作用相當(dāng)于是給變量取了“別名”。引用本身沒有自己的內(nèi)存地址,因此在定義的過程當(dāng)中必須進(jìn)行初始化,而且一旦和變量進(jìn)行了綁定,則無法解綁并重新綁定。另外,左值引用只能和左值進(jìn)行綁定。
常引用
常引用是指利用 const 修飾的引用類型。由于引用本身已經(jīng)綁定不可解綁,因此所用的 const 引用都是底層 const,即引用對象不能改變(俗稱常引用)。由于常引用引用的是常量,所以可以使用表達(dá)式,字符串字面值等常量為常引用進(jìn)行初始化。
右值引用
右值引用是 C++ 新標(biāo)準(zhǔn)中引入的。右值引用的定義形式為:
右值引用的主要作用有兩個:
實現(xiàn)移動語義
實現(xiàn)完美轉(zhuǎn)發(fā)
作用一、移動語義的實現(xiàn)。
C++ 中的 “移動語義” 是相對于 “拷貝語義” 的一個概念。在引入”移動語義“之前,C++ 主要利用拷貝功能來實現(xiàn)對象的移動,通常的操作是在新分配好的內(nèi)存空間上調(diào)用拷貝構(gòu)造函數(shù),將舊對象復(fù)制到新內(nèi)容中,然后再釋放舊對象。而”移動語義“的引入使得 C++ 允許直接將舊對象的所有權(quán)直接轉(zhuǎn)讓給新分配的內(nèi)存空間,這樣一來避免了舊對象的復(fù)制與釋放,極大地提高了代碼的運行效率。如果說 ”拷貝語義“ 描述了對象的拷貝功能,那么”移動語義“ 描述的則是對象的剪切功能。(注:轉(zhuǎn)讓意味著將所有權(quán)交給新區(qū)域的同時,還要放棄自身的所有權(quán))
C++ 中的 ”移動語義“ 主要利用右值引用來實現(xiàn)。C++ 允許將一個右值引用 rr 綁定到一個臨時對象上,從而將該臨時對象的生命周期延長到與 rr 一樣長。這樣一來,我們便可以通過 rr 在程序當(dāng)中的任何位置訪問該臨時對象。
作用 二:完美轉(zhuǎn)發(fā)
完美轉(zhuǎn)發(fā)主要應(yīng)用于這樣一組場景:我們需要將一個函數(shù)的參數(shù)原封不同地傳遞給另一個函數(shù)。在 C++ 中,函數(shù)參數(shù)除了類型和值以外,還有 const 和 non-const 以及 左值和右值 兩組屬性。所謂的完美轉(zhuǎn)發(fā),就是在將一個函數(shù)的參數(shù)傳遞給另一個參數(shù)時,參數(shù)的類型、值以及所有屬性都不能改變,這在泛型編程當(dāng)中有著廣泛的應(yīng)用。
針對模板函數(shù)的實現(xiàn),C++ 11 在原有的正常綁定規(guī)則上增加一個新的綁定規(guī)則 —— 引用折疊,具體規(guī)則如下:
若模板函數(shù) func 的形參類型為 T&, 則不論傳遞給 func 的實參是左值還是右值,一律統(tǒng)統(tǒng)折疊為左值引用
若模板函數(shù) func 的形參類型為 T&&, 則傳遞給函數(shù)的左值實參折疊為左值引用,右值實參折疊為右值引用
總之一句話:在定義函數(shù)模板時使用右值引用做形參,能夠保證參數(shù)的屬性(const 屬性和 左/右值屬性)不發(fā)生變化。
在 代碼 2 中,雖然 i 和 ci 是左值,但由于發(fā)生了引用折疊,所以 f1(i) 和 f1(ci) 是合法的。在引用折疊的規(guī)則下,無論一個函數(shù)有多少個參數(shù),借助右值引用我們都只需要定義一個函數(shù)模板即可,這就大大減少了編程時的低效勞動。
右值引用和常引用的異同
相同點:
右值引用和常引用都允許和表達(dá)式,字符串字面值等進(jìn)行綁定
不同點:
常引用可以和普通變量綁定,而右值引用只能綁定到右值上面
常引用類型的變量是不可修改的,而右值引用類型的變量是可以修改的
引用和指針的區(qū)別
引用和指針都是復(fù)合類型,但指針是變量,有自己的內(nèi)存地址,而引用是別名,沒有自己的內(nèi)存地址
引用一旦定義就必須初始化,而指針可以先定義,然后再初始化
引用一旦和某個具體的變量綁定后,就不能再解除綁定,而指針變量在指向某一變量后,依然可以改變指向。
存在多維指針,但不存在多維引用【C++ 中不允許直接定義引用的引用,但可以通過類型別名或者模板類型參數(shù)間接定義】
因為引用沒有自己的內(nèi)存地址,所以只存在引用指針的引用,但不存在指向引用的指針
注:實際上,C++ 當(dāng)中引用的實現(xiàn)也是利用到了 const 指針來實現(xiàn)的,但是由于編譯器做了一部分工作,因此引用本身對于程序員而言是透明的。