• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            左值和右值(轉)

            Posted on 2012-12-16 15:08 鑫龍 閱讀(322) 評論(0)  編輯 收藏 引用 所屬分類: c++

            左值右值

            左值(lvalue)和右值(rvalue)是編程中兩個非常基本的概念,但是也非常容易讓人誤解,看了很多文章,自我感覺真正將這個問題講的很透徹的文章還沒有看見,所以自告奮勇來嘗試一下。如果左值右值的概念不是非常清楚的話,它們遲早會像攔路虎一樣跳出來,讓你煩心不已,就像玩電腦游戲的時候每隔一段時間總有那么幾個地雷考驗你的耐性,如果一次把所有地雷掃盡就好了。:)

            左值(lvalue)和右值(rvalue)最先來源于編譯理論(感謝南大小百合的programs)。在C語言中表示位于賦值運算符兩側的兩個值,左邊的就叫左值,右邊的就叫右值。比如:

            int ii = 5;//ii是左值,5是右值

            int jj = ii;//jj是左值,ii是右值

            上面表明,左值肯定可以作為右值使用,但反之則不然。左值和右值的最早區別就在于能否改變。左值是可以變的,右值不能變。【注1

            1:這一點在C++中已經豬羊變色,不再成立。拱豬游戲還是挺好玩的,我還真抓過好幾次全紅心,不過真的好險。:)

            在很多文章中提到,在C++中,左值更多的指的是可以定位,即有地址的值,而右值沒有地址。【注2

            2:這一點仍然不準確,我在程序中生成一個臨時右值std::vector(),你能夠說它沒有地址嗎?難道它是沒有肉體的鬼魂或幽靈?它是有地址的,而且它也是絕對的右值。

            在現代C++中,現在左值和右值基本上已經失去它們原本所具有的意義,對于左值表達式,通過具體名字和引用(pointer or reference)來指定一個對象。非左值就是右值。我來下一個定義:

            左值表示程序中必須有一個特定的名字引用到這個值。

            右值表示程序中沒有一個特定的名字引用到這個值。

            跟它們是否可以改變,是否在棧或堆(stack or heap)中有地址毫無關系。

            1.左值

            在下面的代碼中:

            int ii = 5;

            int const jj = ii;

            int a[5];

            a[0] = 100;

            *(a+3) = 200;

            int const& max( int const& a, int const& b ) //call by reference

            {

                  return a > b ? a : b;

            }

            int& fun(int& a) //call by reference

            {

                  a += 5;

               return a;

            }

            iijja[0]*(a+3),還有函數max的返回值比如max(ii, jj),【注3】函數fun的返回值fun(ii)都是左值。,它們都是有特定的引用名字的值。iijja[0]*(a+3)max(ii, jj)fun(ii)分別就是它們的名字。

            3:在這里有一個不太容易分清楚的盲點。那就是有人會問max(8, 9)到達是左值還是右值,C++標準規定常量引用(reference to const)可以引用到右值,所以max(8, 9)似乎應該是右值,不過不管它是左值,還是右值,我們都不能試圖去改變它。為了與前面的概念一致,我認為它是左值,不可改變的常量左值。

            左值有不能改變的,即被const所修飾的左值,比如上面的jjmax(ii, jj)都是被常量(const)魔咒所困住的左值。

            沒有被const困住的左值當然是可以改變的,比如下面的代碼都是成立的:

            ii = 600;

            a[0] = 700;

            fun(ii) = 800; //OK!

            我們的眼睛沒有問題,fun(ii) = 800;完全正確,因為它是可以改變的左值。所以我們看STL的源碼,就會理解std::vector中的重載operator[]運算符的返回值為什么要寫成引用,因為operator[]必須返回左值。

             

            2.右值

            沒有特定名字的值是右值。先看下面的代碼:

            std::list();

            std::string(“It is a rvalue!”);

            int fun1() //call by value

            {

                  

            }

            int* fun2() //call by reference

            {

                  

            }

            其中std::list()std::string(“It is a rvalue!”),函數fun1的返回值fun1(),函數fun2的返回值fun2()都是右值,它們的值都沒有特定的名字去引用。也許有人會奇怪,fun2()也是右值?最前面的max(a,b)不是左值嗎?

            請看清楚,函數fun2的返回值是pointerpointer也是call by value,而函數max的返回值是referencereferencecall by reference。所以說C++中引入reference不僅僅是為了方便,它也是一種必須。【注4

            4Scott Meyer寫的《More Effective C++》的條款1專門講了pointerreference的區別,寫的很好,辨別的非常清楚。

            fun2()是右值,但 *fun2()卻是左值,就跟經常看到的*p一樣,所以看C++庫代碼的時候,會發現重載operator*的函數返回值是reference

            當然我還遺漏了一種右值,那就是字面上的(literal)值,比如58.23’a’等等理所當然的都是右值。

            右值最初出現的時候,一個最大的特征就是不可改變。但就跟我們的道德標準一樣,時代不同了,標準也變化了,以前的三綱五常早已經被扔到歷史的垃圾堆里面了。

            C++中有可以改變的右值,而且這個特性還非常有用。那就是用戶自定義的類(class)的構造函數生成的臨時對象。比如:

            std::vector(9)std::deque(),……都是可以改變的右值。在Herb Sutter的《More Exceptional C++》中的條款7page51頁有這樣幾行代碼:

            // Example 7-2(b): The right way to shrink-to-fit a vector.

            vector<Customer> c( 10000 );

            // ...now c.capacity() >= 10000...

            // erase all but the first 10 elements

            c.erase( c.begin()+10, c.end() );

            // the following line does shrink c's

            // internal buffer to fit (or close)

            vector<Customer>( c ).swap( c );

            // ...now c.capacity() == c.size(), or

            // perhaps a little more than c.size()

            認真看幾遍,你會發現但vector的大小增大到一定程度,你又用不著這么多空間的時候,你會想辦法把它收縮到最合適的大小,但利用別的辦法比如調用成員函數reserve()都無法辦到,這個時候就必須利用右值可以改變這個性質了。

            vector<Customer>( c ).swap( c );這行代碼就是點睛之處。

            首先使用復制構造函數生成臨時右值vector<Customer>( c ),這個右值正好是合適大小,然后和c交換【注5】,c就變成合適大小了,最后在整個表達式結束的時候,這個臨時右值析構歸還內存空間。真是紳士一般的優雅!

            5:這個時候這個臨時右值就發生了改變。

            如果還不理解,可以看看書,或者直接看庫的源代碼。

            至于為什么會這樣?我思考了一下,我想是這樣的,我們看類(class)的數據布置結構,會發現它的每一個數據成員都是有名字的,我想編譯器在編譯的過程中,都會生成一個外部不所知的對這個臨時對象右值的名字引用,但需要改變這個臨時對象的時候,這個名字就用上了。比如:

            class Point

            {

            public: //純粹為了方便,我把數據成員公開,現實中盡量不要這樣用

                  int x, y ,z;

                  ……//其他各種成員函數

            };

            我們現在就可以改變右值,用到了匿名的引用名字。

            Point().x = 6;//改變了右值

            Point().y = 6;//同意改變了右值,不過注意,這個右值跟上面的不是同一個。

            總結

            左值和右值的真正區別我想就是這些了,左值表示有特定的名字引用,而右值沒有特定的名字引用。當然我仍然會有疏忽,希望大家能夠提醒我,指正我的不足。

            前兩天看Herb Sutter從郵件中寄來的新文章(我訂閱他的新文章郵件通知),一篇是講Tuple數據結構的,沒有什么新意,以前好像看過,還有一篇名字是:(MostlyPrivate,地址為http://www.cuj.com/documents/s=8273/cujcexp2107sutter/ 內容本身并不深,但看完文章,發現隨處可見C++的波詭云譎,又會對什么叫袖里乾坤,滴水藏海多一份感性認識。

            在下一篇文章我想從不同于一般的角度,從自己的經歷談談在校畢業生在IT行業怎樣找工作,我想會讓所有讀者都有一些思考,不僅僅是求職者。題目我已經想好了,就叫《扮虎吃豬》,不過現在我有一些別的事情要忙,所以可能會讓大家等幾天。

            轉載請注明來源,謝謝!

            吳桐寫于2003.6.20

            最近修改2003.6.21

            轉自:
            http://blog.csdn.net/csdnji/article/details/169200

            精品久久人妻av中文字幕| 办公室久久精品| 亚洲AV无码久久精品蜜桃| 麻豆AV一区二区三区久久| 激情伊人五月天久久综合| 激情五月综合综合久久69| 久久精品亚洲AV久久久无码| 久久精品国产精品亚洲毛片| 91精品免费久久久久久久久| 精品国产99久久久久久麻豆| 亚洲国产精品久久久久久| 亚洲欧美国产日韩综合久久| 国产亚洲色婷婷久久99精品| 久久久久亚洲av成人无码电影 | 日日躁夜夜躁狠狠久久AV| 一本一道久久精品综合| 日日噜噜夜夜狠狠久久丁香五月| 狠狠色丁香婷婷综合久久来来去| 午夜精品久久久久久99热| 久久人人爽人人澡人人高潮AV| 久久精品中文闷骚内射| 狠狠色婷婷久久综合频道日韩| 国产精品va久久久久久久| 久久精品国产91久久综合麻豆自制| 中文成人久久久久影院免费观看| 97久久超碰国产精品2021| 久久99久国产麻精品66 | 99精品国产综合久久久久五月天| 国产精品综合久久第一页| 77777亚洲午夜久久多喷| 亚洲国产成人精品91久久久| 精品久久久久国产免费| 精品欧美一区二区三区久久久| 久久线看观看精品香蕉国产| 丰满少妇高潮惨叫久久久| 国产精品美女久久久久| 国产精品久久久久久影院| 久久精品国产精品亚洲精品| 久久99国产精品久久99| 国产成人久久久精品二区三区| 精品国产乱码久久久久久浪潮|