C++|左值、右值、將亡值和引用的概念|聊聊我對它們的深入理解-創(chuàng)新互聯(lián)

文章目錄
    • 前言
    • 左右值的辨析
      • 一個特殊的問題
      • 將亡值
    • 引用的深刻理解
      • 右值引用是右值嗎???

成都創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站設計、成都網(wǎng)站建設與策劃設計,華亭網(wǎng)站建設哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設十年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:華亭等地區(qū)。華亭做網(wǎng)站價格咨詢:13518219792前言

這篇文章是我在探究完美轉(zhuǎn)發(fā)這個語法點時,引發(fā)的相關問題思考,為了使自己的理解更深刻,故寫下這篇博客

左右值的辨析

首先需要明白兩個概念:類型(type)和值類別(value category),看似差不多的兩個概念其實毫不相干。類型指的是數(shù)據(jù)類型,int,char這樣的內(nèi)置類型,類型主要是用來區(qū)別它們的字節(jié)大小。除了內(nèi)置類型還有自定義類型,自定義類型中的類型還表征了結構,像C語言的結構體,由于結構(或者說內(nèi)置類型的順序)的不同引發(fā)的內(nèi)存對齊問題。所以類型表征的是大小,結構,表征這個數(shù)據(jù)是怎樣的(how?)

而值類別呢,就是關于變量的左右值屬性,先說結論,我認為值類別表征了數(shù)據(jù)的存儲位置(where?),左右值也是第一個需要辨析的重要概念。在之前寫的博客中,我說可以通過是否能取地址判斷左右值。如果能取地址,說明這個變量是左值,我們可以通過地址修改它,如果不能取地址,則變量是右值,我們不能通過地址修改它。比如int num = 10;這行代碼,將10存儲到變量num中,num對應一個地址,后續(xù)可以通過地址修改num的值,所以我們稱num為左值(num在表達式的左邊,這是左的含義),表達式右邊的10就是一個右值,我們無法通過10的地址修改10。

以上的分析是從高級語言的角度展開的,之前我只能做到這樣的理解,但是現(xiàn)在我們可以從一個更高的角度理解左右值,從計算機體系結構的角度理解int num = 10;這行代碼,在語言層面,它表示創(chuàng)建一個int類型的變量num,并初始化為10。但跳出高級語言,全新的理解是:num才不是什么變量名,num對應了一個地址,是位于進程地址空間上的棧區(qū)的地址,int也不是什么類型,int表示從該地址往后8字節(jié)的空間被進程使用了,要以8字節(jié)為一個整體,修改該地址上的內(nèi)容。而10呢?它是一個字面常量,用二進制表示為00001010,根據(jù)賦值對象的不同再進行提升或截斷,比如10要賦值給int對象,所以被提升為00000000 … 00001010(前面多出7個全0的序列,每個序列有8個0),存儲時再根據(jù)大小端字節(jié)序?qū)⑦@些字面值從代碼區(qū)拷貝到剛才的棧區(qū)地址上。

繼續(xù)分析,這行代碼被編譯后會被放到進程地址空間的正文代碼區(qū),系統(tǒng)怎么知道你要用10初始化num?因為正文代碼區(qū)的存儲了10的二進制序列,代碼區(qū)中還有10要放入的地址(沒有什么num,只有一串地址),以及把10放到地址上的指令,這些信息都會在代碼中表示。并且,程序的正文代碼區(qū)也是有地址的,代碼區(qū)存儲系統(tǒng)要執(zhí)行的指令?,F(xiàn)在回頭看右值的概念,不能取地址的就是右值,10有地址嗎?當然有,沒有地址系統(tǒng)怎么訪問正文代碼區(qū),怎么知道你要初始化的值是10,所以不能取地址不是因為沒有地址,而是因為這個地址你不能知道,地址位于只讀數(shù)據(jù)區(qū)(代碼區(qū)的數(shù)據(jù)可不能隨便修改,當然是只讀數(shù)據(jù)區(qū))或者說該地址上的數(shù)據(jù)只有在程序運行后才會被系統(tǒng)讀取,你要取地址,編譯器直接出手,誰能保證你不會做一些危害系統(tǒng)安全的事,編譯器可不會給你這些地址,于是程序編譯失敗。

所以你看,直接創(chuàng)建的局部變量,全局變量,new出來的變量都是左值,為什么?就是因為棧區(qū),堆區(qū),靜態(tài)區(qū)都是系統(tǒng)允許你訪問的區(qū)域,我們對這些區(qū)域擁有寫入的權限,所以系統(tǒng)可以給你它們的地址。但是像什么字面常量,臨時變量(隱式類型轉(zhuǎn)換表達式產(chǎn)生的中間值,函數(shù)返回產(chǎn)生的中間值…),匿名對象就是右值,因為程序編譯后,它們位于代碼區(qū)或者你沒有修改這些數(shù)據(jù)的必要,所以系統(tǒng)才不會把地址給你,這些空間就像系統(tǒng)的私人空間,你不能隨便的訪問,只有在程序運行后,為了運行程序,系統(tǒng)才會訪問這些空間

最后總結一下,不能取地址就是右值的說法有些不準確,或者說我不太認同這種說法,我認為只要數(shù)據(jù)位于的區(qū)域你沒有權限訪問,這些數(shù)據(jù)就是右值,你有權限訪問的區(qū)域,存儲的數(shù)據(jù)是左值。并且這個權限不是語言限制的,而是系統(tǒng)限制的訪問權限,語言位于系統(tǒng)之上,我們可以突破語言的限制,但是底層系統(tǒng)的限制我們無法突破,也不能突破

一個特殊的問題

字符串字面值是左值(?)

如果以是否能取地址作為左右值判斷的標準,那么字符串字面值確實左值
在這里插入圖片描述
比如"abc"這個字符串,我們寫一段程序,輸出它的地址,make編譯這份源文件,結果是可以編過的,再運行可執(zhí)行文件,地址也被正常的打印出來。但是這個地址是什么類型的呢?由于g++編譯器的typeid打印結果不好觀察,這里我使用vs的編譯環(huán)境,使用typeid打印"abc"字符串的類型與其地址的類型
在這里插入圖片描述
可以看到字符串字面值的類型是const修飾的char數(shù)組,大小為4(最后有個’\0’),這里又涉及到const修飾值的問題,先不管它?;氐阶铋_始的問題,以是否能修改作為判斷標準,字符串字面值還是左值嗎?我們先看一下字符串字面值位于地址空間的哪個區(qū)域
在這里插入圖片描述

這是進程地址空間的劃分,下面是低地址,上面是高地址,我們可以通過打印初始化全局數(shù)據(jù)區(qū)變量的地址,棧區(qū)變量以及堆區(qū)變量的地址,判斷"abc"這個字符串是存儲在哪塊區(qū)域的

#includeusing namespace std;
int c = 10;

int main()
{int a = 10;
    int* b = new int(10);
    cout<< "棧區(qū)變量的地址      :"<< &a<< endl;
    cout<< "堆區(qū)變量的地址      :"<< b<< endl;
    cout<< "初始化全局變量的地址:"<< &c<< endl;
    cout<< "字符串字面值的地址  :"<< &("abc")<< endl;

    return 0;
}

在這里插入圖片描述
結果很明顯,棧區(qū)向下增長,地址最高,堆區(qū)向上增長,地址次高,初始化全局數(shù)據(jù)區(qū)的地址在兩者之下,而字符串字面值的地址比初始化全局數(shù)據(jù)區(qū)還低,通過進程地址空間的劃分,我們可以得知字符串字面值被存儲在正文代碼區(qū)。因此,程序被編譯為可執(zhí)行文件后,"abc"這個字符串被存儲在了正文代碼區(qū)。與字符串數(shù)組和通常的字面常量不同,字符串數(shù)組在程序運行之后才被存儲到棧區(qū)或者堆區(qū)中(從代碼區(qū)中拷貝到其他可修改的區(qū)域),雖然通常的字面常量在程序被編譯好后就被存儲在了正文代碼區(qū),但是它沒有表征具體信息的字段,比如數(shù)據(jù)的類型,有幾個字節(jié),但是字符串字面值是有的,代碼區(qū)中有信息表示它的類型,大小,所以我們可以根據(jù)這些信息使程序打印出字符串字面值的地址的類型。

但是我們可以通過這個地址修改字符串字面值的值嗎?我想的是,雖然可以通過強制類型轉(zhuǎn)換去除變量的const屬性,但是字符串字面值存儲在正文區(qū),正文區(qū)的數(shù)據(jù)肯定不能修改,所以我認為字符串字面值是右值,但是我一搜索“修改字符串常量”就被這篇文章打臉,仔細一看,文章并不簡單,其中的修改方法是從系統(tǒng)角度修改頁的權限,得到代碼區(qū)的寫入權限,所以可以修改字符串字面值。如果從系統(tǒng)的角度出發(fā),我們可以直接修改頁的權限,獲取可讀數(shù)據(jù)區(qū)的寫權限,那么所有的數(shù)據(jù)都是可寫的,所有的數(shù)據(jù)都是左值?顯然我們不能這樣理解,我們應該從高級語言的角度上理解,代碼區(qū)的數(shù)據(jù)就是不可修改的,我們對代碼區(qū)只有讀權限,因此字符串字面值是右值。這個結論與網(wǎng)上的大多數(shù)結論相反,究其原因,只是我對左右值的判斷依據(jù)與大部分人不同,我認為不能簡單的將左右值用是否能取地址來區(qū)分,這只是方便初學者理解的一種說法,學習到現(xiàn)在,我認為區(qū)分左右值的依據(jù)應該是是否能在語言層面上修改數(shù)據(jù),能修改的數(shù)據(jù)就是左值,不能修改的數(shù)據(jù)就是右值,而是否能修改的本質(zhì)是我們對地址空間的權限,對正文代碼區(qū)只有讀權限,對棧區(qū),堆區(qū)以及靜態(tài)區(qū)我們有讀寫權限,無論語言怎么限制(這里點名const),我們都能通過一些特殊手段,突破這個限制,繞過編譯器的檢查,非法的篡改被語言級別限制的數(shù)據(jù)。比如函數(shù)的返回值,雖然返回值是一個臨時變量,具有常屬性(這是語言級別的限制),但是它還是存儲在棧區(qū),我們當然可以非法篡改,具體可以看函數(shù)棧幀理解這篇文章。

將亡值

在這里插入圖片描述
有意思的是,由于C++11引入了右值引用,將亡值這一概念隨之被提出,我們探討的對象又復雜了起來。剛才我所說的左值與右值對應著圖片上的lvalue(傳統(tǒng)意義上的左值)和rvalue(傳統(tǒng)意義上的右值),rvalue中的r除了right的意思,還可以理解為read,表示只讀。而rvalue包括了將亡值xvalue和純右值prvalue,由于將亡值的出現(xiàn),我們需要將這些概念重新梳理一遍

glvalue(泛左值)= lvalue(傳統(tǒng)意義上的左值)+ xvalue(將亡值)
rvalue(傳統(tǒng)意義上的右值)= prvalue(純右值)+ xvalue(將亡值)

我們通常討論的左值并不是gvalue(泛左值),而是lvalue,通常討論的右值是rvalue,它包含了將亡值xvalue和純右值pvalue,其中的將亡值與右值引用息息相關,匿名對象和函數(shù)返回值都是將亡值,它們都具有常屬性,并且生命周期較短,在下一條語句執(zhí)行前資源就會被釋放。具體的比如隱式類型轉(zhuǎn)換產(chǎn)生的中間變量,為了調(diào)用類的函數(shù)而定義的匿名對象,這些變量似乎都是工具人,被創(chuàng)建只是為了其他語句的成功執(zhí)行??梢灶A見的是這些將亡值都不是字面常量,而是程序運行后,在棧上,堆上創(chuàng)建的變量,雖然這些變量都有地址,但是我們沒有必要知道這些地址,因為它們都是程序運行中產(chǎn)生的中間值,被創(chuàng)建只是為了完成其他代碼,當代碼執(zhí)行完,將亡值就會被釋放,它默默地來,也默默地走,編譯器甚至不讓我們知道它們的“姓名”。根據(jù)將亡值存儲在可修改數(shù)據(jù)區(qū)這一條件,我們能得到將亡值是一個左值的結論,我們可以通過一些特殊手段修改將亡值,雖然編譯器為將亡值添加了限制,我們無法修改將亡值(將亡值的生命周期太短了,一行代碼執(zhí)行完就被釋放,所以要修改它的值也是比較困難的,但是將亡值存儲在可修改的數(shù)據(jù)區(qū)上,理論上我們是可以修改的,就像我的文章中對函數(shù)返回值的篡改一樣)。

高級語言中,這樣的中間值肯定是不允許使用者修改的,為了程序的正確運行,我們也沒有修改的必要,所以語言對這樣的中間值加了限制,我們無法修改它們的值,一個典型的例子,int &x = 1.1;這行代碼肯定是無法通過編譯的,分析一下,假設現(xiàn)在的代碼是int x = 1.1; 將一個浮點數(shù)賦值給一個整形,兩者的數(shù)據(jù)存儲規(guī)則都不一樣,肯定是無法直接賦值的,這里要發(fā)生隱式類型轉(zhuǎn)換,將1.1這個浮點數(shù)轉(zhuǎn)換成int類型的數(shù)據(jù),用一個中間變量接收轉(zhuǎn)化的結果,最后再把中間變量的值賦給x,這個過程中誰是工具人已經(jīng)很明顯了。這就是將亡值的產(chǎn)生原因,如果代碼是int &x = 1.1;呢?這個x只是一個引用,并不是一個變量,所以無法接收數(shù)據(jù),但最后引用是1.1的引用嗎?x的類型是int類型的引用,int類型的引用肯定無法引用浮點數(shù),所以x是中間變量的引用,但是我們可以訪問中間變量嗎?不行,你想訪問。編譯器直接出手,這就是語法的規(guī)則限制:無法訪問將亡值。

但是C++這門語言非常自由,我們可以直接訪問內(nèi)存啊,只要知道中間變量在內(nèi)存中的地址,我們就可以修改,哪管什么編譯器的限制,編譯器只會限制明顯的修改將亡值的情況,我們不通過變量直接修改將亡值就可以繞過編譯器的檢查。具體可以看我對函數(shù)返回值的篡改這篇文章,C++允許你做很多細致的操作,只要你遵守它的規(guī)則,就能用C++做很多事,它會非常好用。但是自由也是有代價的,規(guī)則的限制只能限制那些本就會遵守規(guī)則的人,無法限制那些無視規(guī)則的人。
在這里插入圖片描述
現(xiàn)在,我想這張圖也能解釋清楚了,為什么將亡值即屬于glvalue(泛左值),還屬于rvalue(傳統(tǒng)意義上的右值),這兩個看似矛盾的歸類,其實就是由自由導致的,C++成也自由,敗也自由,如果你遵守C++的規(guī)則,那么將亡值就是右值,語言限制你不能去修改它,一些直接修改的操作不被編譯器允許,即使存儲將亡值的數(shù)據(jù)區(qū)是可寫的,但是遵守規(guī)則導致的就是將亡值的無法修改,將其視為右值也是有理有據(jù)的。但是你不遵守C++的規(guī)則,你就可以修改將亡值,誰讓將亡值存儲在了可寫的數(shù)據(jù)區(qū),將其視為左值也是沒問題的。綜上,我們可以認為由于C++這門語言的自由,誕生了即是左值又是右值的矛盾的值類別:將亡值。

而純右值prvalue就是字面常量,1,‘a(chǎn)’,這樣的數(shù)據(jù),它們被編譯器編譯后就是一些二進制數(shù)據(jù),被嵌入到代碼區(qū)中,作為代碼的一部分,我們無法在語言層面上修改代碼區(qū)的數(shù)據(jù)。lvalue(傳統(tǒng)意義上的左值)呢,就是我們經(jīng)常定義的變量,有名字的變量,就算被const修飾,我們依然可以修改它們的值,誰讓它們存儲在可寫的數(shù)據(jù)區(qū)呢?但是除了這兩個典型的左右值,還有一些工具人,它們被叫做將亡值,至于它的值類別是屬于左值還是右值,我們無法做出具體的分類界定,它的左右值屬性將由使用者決定

引用的深刻理解

(int &x = 1.1;這行代碼涉及到引用,所以再補充一下我對引用的理解)

相較于C語言,C++引入了一種語法:引用,這篇文章不談引用的基本使用,我們需要深刻的理解為什么C語言沒有引用,而C++有呢?因為它比指針使用方便,不需要寫&和*嗎?確實這是一個方面,但是這只是引用的一種語法表現(xiàn),并不是引用的出現(xiàn)原因。在Linux文件系統(tǒng)中,最頂層的文件對應著底層結構中的一個inode文件,inode作為文件的唯一標識符,一份文件只有一個inode編號,但是可以有很多頂層文件使用這個inode編號,用不同的文件名映射相同的inode,這就是硬鏈接。還有網(wǎng)絡中的進程與端口號之間的關系,通過端口號肯定能找到一個進程,并且只能找到一個進程,就像文件名一樣,不同的端口號可以指向同一個進程,進程就是底層唯一的結構,不管上層怎么變,進程只有一個,而端口號隨便幾個。端口號和文件名就有了一些解耦的意思,用戶不能(不用)直接接觸底層的結構,而是接觸較高層的一些結構,不僅降低使用成本,還減少了用戶直接接觸底層結構會帶來的風險。這樣的加一層在軟件設計中非常的常見,說一個我個人的觀點,我認為C++的引用也有點這樣的意思,語言的設計者鼓勵我們多使用引用,而少使用或者不使用指針,就是為了減少使用者對底層結構(地址)的直接接觸,將使用者與地址解耦,減少直接接觸地址可能存在的風險。當然,這只是左值引用的理解,還有一個右值引用。通過左值引用我們知道,可以通過上層的結構(引用)接觸底層結構(地址)上的數(shù)據(jù),而右值包括了純右值和將亡值,對于純右值,由于其存儲在代碼區(qū),我們不能修改它的地址,所以右值引用會對被引用的右值做一個拷貝,將數(shù)據(jù)拷貝到可寫數(shù)據(jù)區(qū)中,用戶對右值引用的修改變成了對一份拷貝的修改(右值引用是可以修改的,不了解的讀者可以寫簡單的代碼驗證一下),畢竟不能修改代碼區(qū)上的數(shù)據(jù)是系統(tǒng)規(guī)定的,語言不能脫離系統(tǒng)設計啊。而對于將亡值的右值引用,就涉及到移動構造和移動拷貝的問題,這是C++11帶來的一塊語法糖,如果后續(xù)代碼還要使用將亡值所擁有的資源,我們可以用右值引用作為函數(shù)形參,定義一個移動構造或者移動賦值函數(shù),將工具人的資源轉(zhuǎn)移到自己的左值上,這里心疼將亡值1秒鐘,由于移動構造和移動賦值不是我們的重點,我只是簡單的提一下。

所以引用就是別名,不是變量的別名,而是地址的別名,是與地址建立的一種映射關系。我們可以通過不同的別名訪問同一塊地址空間,并且由于引用的書寫比指針簡單,在C++中能使用引用就不使用指針了,使用者不直接接觸地址,程序出錯的可能也就小了,C++設計者的心思已經(jīng)被我們狠狠拿捏,畢竟引用的理解和書寫比起指針真的太簡單了,初學者為什么要在地址中繞來繞去,直接用引用代替指針,學習成本不就減低了嗎?
在這里插入圖片描述
lea的意思是加載有效的偏移量地址,先看左值引用的那三行代碼,int y = 1是將1放到ebp-24h這個地址處,int& z = y則是創(chuàng)建y的引用z,我們知道引用的本質(zhì)是與地址建立映射關系,那么這個映射關系當然就需要保存了,創(chuàng)建引用的第一條匯編就是將ebp-24h這個偏移地址存儲到eax寄存器中,ebp-24h這個偏移地址上存儲的是什么呢?從int y = 1的匯編可以得知ebp-24h這個地址與變量y的地址相同,存儲的是1。再看創(chuàng)建引用的第二條匯編,將eax中存儲的數(shù)據(jù)移動到ebp-30h中,eax存儲的不就是變量y的地址嗎,將y的地址存儲到ebp-30h不就是映射關系的保存嗎?由于x86架構的系統(tǒng)有32位的地址,所以y的地址被存儲到dword類型(雙字,32比特)的地址上。再看最后一行代碼z = 2,它的匯編有兩條,第一條是將ebp - 30h地址處的數(shù)據(jù)移動到eax寄存器中,也就是將變量y的地址,存儲1的地址移動到eax寄存器中,最后再將2移動到eax保存的地址處。這么看來,雖然在C++中我們沒有顯式的書寫指針,但是在匯編層面依然是需要使用指針的,可以說從匯編的角度看,引用與指針沒有任何區(qū)別,指針保存了變量的地址,引用需要保存與被引用對象的地址的映射關系,但這也是地址啊,我們通過引用找到被引用對象的地址,不就是引用與被引用對象之間建立起了映射關系嗎?引用和指針都是保存對象的地址,那么兩者有區(qū)別嗎?很好理解,雖然兩者的底層相同,但是在高級語言的層面上,引用是對地址的一種封裝,而指針呢?指針沒有封裝地址,直接保存了地址,將地址暴露了出來。

再看右值引用的兩行代碼,int&& x = 1是創(chuàng)建一個右值引用,引用1這個字面常量,我們知道程序編譯后,字面常量被存儲在代碼區(qū),代碼區(qū)對我們來說是只讀的,我們不能修改上面的數(shù)據(jù),而引用呢,引用是對地址的一種封裝,但是引用可以封裝一個只讀數(shù)據(jù)區(qū)的地址嗎?當然不能,你也沒見過&1這樣的表達式吧,那么引用封裝的地址又是什么地址呢?由于只讀數(shù)據(jù)區(qū)的數(shù)據(jù)不能修改,所以編譯器會將代碼翻譯為:先在可寫數(shù)據(jù)區(qū)創(chuàng)建只讀數(shù)據(jù)的一份拷貝,再把這份拷貝的地址給引用,讓引用封裝這個地址。所以我們通過引用修改的數(shù)據(jù),只是只讀數(shù)據(jù)的一份拷貝,并不是真正的只讀數(shù)據(jù)。因此第一條匯編就是將1移動到ebp-18h地址處,顯然這是在棧上開辟了空間存儲1,接著再把1的偏移地址ebp-18h加載到eax寄存器中,最后把eax寄存器的數(shù)據(jù)移動到ebp-0ch地址處。可以看出,int&& x = 1這條代碼干了兩件事,一是int x = 1在棧區(qū)創(chuàng)建一個int變量存儲1,然后再將存儲1的地址保存到棧區(qū)的另一塊空間,作為引用的映射關系保存,仔細一看,第二件事不就是左值引用的創(chuàng)建過程嗎?

右值引用是右值嗎???

這是一個讓我困擾很久的問題,先給出答案,右值引用不是右值,它是一個左值,直接看代碼

void print(int& x)
{printf("void print(int& x)\n");
}

void print(int&& x)
{printf("void print(int&& x)\n");
}

int main()
{int x = 0;
	int& y = x;
	int&& z = 1;
	print(y);
	print(z);
	return 0;
}

print有兩個重載的版本,一個是形參類型為左值引用,一個為右值引用,將左值引用y作為print的參數(shù),調(diào)用的是形參為左值引用的print,這沒什么問題,那么將右值引用z作為print的形參,會調(diào)用形參為右值引用的print嗎?
在這里插入圖片描述
這也能從側(cè)面說明右值引用是左值了吧?從剛才講解的匯編我們也能理解,右值引用拷貝了右值,引用的是拷貝后的左值,所以嚴格來說右值引用是左值,用右值引用作為實參不能調(diào)用形參為右值引用的函數(shù)。再來一個例子在這里插入圖片描述
j作為右值引用卻不能引用同為右值引用的兄弟z,但是i作為左值引用卻能引用同為左值引用的y。報錯信息顯式右值引用不能綁定左值,所以z是左值,不是右值引用,這是編譯器說的

但是這里其實隱藏了一個條件,就是是否創(chuàng)建變量去引用右值,什么意思呢?使一個變量名去引用一個右值,這個變量的類型就是右值引用,我們可以通過右值引用得到的變量名,找到可寫數(shù)據(jù)區(qū)中數(shù)據(jù)首地址,通過這個變量名修改地址上的數(shù)據(jù),所以右值引用后得到的變量是一個左值(注意這里側(cè)重引用后的結果是否用變量接收)這才是正確的結論。

而沒有創(chuàng)建變量接收的右值呢?就是一個實實在在的右值了,或者說是一個將亡值,它的資源即將被釋放。你看,一些函數(shù)可以返回右值引用吧,一些表達式的值也是右值引用吧(比如匿名對象的創(chuàng)建),所以一些函數(shù)表達式,匿名對象的表達式它們的運算結果都是右值引用,且右值引用沒有名字,這時的右值引用就是右值了

這個變量名就像一個索引,我們通過變量名修改其指向地址上的數(shù)據(jù),一旦沒有了這個索引,我們就無法通過正常手段修改地址上的數(shù)據(jù),所以說,只要把這個索引暴露出來,索引指向的數(shù)據(jù)就是左值,是可以修改的,但是沒有把索引暴露出來,就說明了其指向的數(shù)據(jù)不想被修改,是一個右值,編譯器也是根據(jù)數(shù)據(jù)是否可以被修改,對代碼進行優(yōu)化以提高程序的運行效率。通過上面的講解,可以說右值引用大部分時間是一個左值,為什么?只有在函數(shù)的返回以及使用匿名對象的過程中,右值引用才是實實在在的右值啊,但是這些過程都非常短,這個時候就不得不提同樣很“短”的將亡值了,我們知道因為C++11提出了右值引用,將亡值這一概念才會被提出,兩者的關系非常緊密,將亡值是右值,右值引用可以引用右值,當然也能引用將亡值了,但是引用后的右值引用卻是一個左值,我們可以通過右值引用修改將亡值的數(shù)據(jù),你看,雖然將亡值被釋放了,但是它的資源卻給了右值引用,一般在構造函數(shù)中,我們將右值引用得到的將亡值資源轉(zhuǎn)移到我們自己對象上。

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

分享名稱:C++|左值、右值、將亡值和引用的概念|聊聊我對它們的深入理解-創(chuàng)新互聯(lián)
文章轉(zhuǎn)載:http://muchs.cn/article0/dhgsoo.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)做網(wǎng)站、外貿(mào)網(wǎng)站建設、響應式網(wǎng)站Google、自適應網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名