C++的參考型別

來源:互聯網
上載者:User

c++比起c來除了多了類類型外還多出一種類型:引用。這個東西變數不象變
量,指標不象指標,我以前對它不太懂,看程式時碰到引用都稀裡糊塗蒙過去。
最近把引用好好地揣摩了一番,小有收穫,特公之於社區,讓初學者們共用。
    引用指的是對一個對象的引用。那麼什麼是對象?在c++中狹義的對象指的是
用類,結構,聯合等複雜資料類型來聲明的變數,如 MyClass myclass,CDialo
g  mydlg,等等。廣義的對象還包括用int,char,float等簡單類型聲明的變數
,如int a,char b等等。我在下文提到“對象”一詞全指的是廣義的對象。c++
的初學者們把這個廣義對象的概念建立起來,對看參考書是很有協助的,因為大
多數書上只顧用“對象”這個詞,對於這個詞還有廣義和狹義兩種概念卻隻字不
提。

一。引用的基本特性

    首先讓我們聲明一個引用並使用它來初步認識引用。
例一:
       1。     int v,k,h;
       2。     int &rv=v;
       3。     rv=3;      //此時v的值也同時變成了3。
       4。     v=5;
       5。     k=rv+2;    //此時k=5+2=7。
       6。     h=12;
       7。     rv=h;
       8。     rv=20;
    第1句聲明了三個對象(簡單變數)。
    第2句的意思是:聲明了一個引用,名字叫rv,它具有int類型,或者說它是
對int類型的引用,而且它被初始化為與int類型的對象v“綁定”在一起。此時r
v叫做對象v的引用。
    第3句把rv的值賦為3。引用的神奇之處就在這裡,改變引用的值的同時也改
變了和引用所綁定在一起的對象的值。所以此時v的值也變成了3。
    第4句把v的值改為5,此時指向v的引用的值也被改成了5。所以第5句的中k的
值是5+2等於7。

    上述5句說明了引用及其綁定的對象的關係:在數值上它們是聯動的,改變你
也就改變了我,改變我也就改變了你。事實上,訪問對象和訪問對象的引用,就
是訪問同一塊記憶體地區。

    第6,7,8三句說明了引用的另一個特性:從一而終。什麼意思?當你在引用
的聲明語句裡把一個引用綁定到某個對象後,這個引用就永遠只能和這個對象綁
定在一起了,沒法改了。所以這也是我用了“綁定”一詞的原因。而指標不一樣
。當在指標的聲明語句裡把指標初始化為指向某個對象後,這個指標在將來如有
需要還可以改指別的對象。因此,在第7句裡把rv賦值為h,並不意味著這個引用
rv被重新綁定到了h。事實上,第7句只是一條簡單的指派陳述式,執行完後,rv和
v的值都變成了12。第8句執行完後,rv和v的值都是20,而h保持12不變。

    引用還有一個特性:聲明時必須初始化,既必須指明把引用綁定到什麼對象
上。大家知道指標在聲明時可以先不初始化,引用不行。所以下列語句將無法通
過編譯:
              int v;
              int &rv;
              rv=v;

    再舉一例:
例二:
          class MyClass
          {
              public:
                  int a;
                  ...
                  ...
          };
          
          MyClass  myclass;
          Myclass& cc=myclass;
          myclass.a=20;          //等價於cc.a=20
          cc.a=60;               //等價於myclass.a=60

    從以上例子可以看到,無論這個對象有多複雜,使用該對象的引用或是使用
該對象本身,在文法格式上是一樣的,在本質上我們都使用了記憶體中的同一塊區
域。
    取一個引用的地址和取一個對象的地址的文法是一樣的,都是用取地址操作
符"&"。例如:
          int i;
          int &ri;
          int *pi=&ri;//這句的作用和int *pi=&i是一樣的。
    當然了,取一個對象的地址和取這個對象的引用的地址,所得結果是一樣的

二。引用在函數參數傳遞中的作用

    現在讓我們通過函數參數的傳遞機制來進一步認識引用。在c++中給一個函數
傳遞參數有三種方法:1,傳遞對象本身。2,傳遞指向對象的指標。3,傳遞對象
的引用。
例三:
          class MyClass
          {
              public:
                  int a;
                  void method();
          };
          
          MyClass  myclass;
          
          void fun1(MyClass);
          void fun2(MyClass*);
          void fun3(MyClass&);

          fun1(myclass);     //執行完函數調用後,myclass.a=20不變。
          fun2(&myclass);    //執行完函數調用後,myclass.a=60,改變了。

          fun3(myclass);     //執行完函數調用後,myclass.a=80,改變了。

          //注意fun1和fun3的實參,再次證明了:使用對象和使用對象的引用
,在文法格式上是一樣的。

          void fun1(MyClass mc)
          {
                mc.a=40;
                mc.method();
          }

          void fun2(MyClass* mc)
          {
                mc->a=60;
                mc->method();
          }

          void fun3(MyClass& mc)
          {
                mc.a=80;
                mc.method();
          }
    我們有了一個MyClass類型的對象myclass和三個函數fun1,fun2,fun3,這三個
函數分別要求以對象本身為參數;以指向對象的指標為參數;以對象的引用為參
數。

    請看fun1函數,它使用對象本身作為參數,這種傳遞參數的方式叫傳值方式
。c++將產生myclass對象的一個拷貝,把這個拷貝傳遞給fun1函數。在fun1函數
內部修改了mc的成員變數a,實際上是修改這個拷貝的成員變數a,絲毫影響不到
作為實參的myclass的成員變數a。
    fun2函數使用指向MyClass類型的指標作為參數。在這個函數內部修改了mc所
指向的對象的成員變數a,這實際上修改的是myclass對象的成員變數a。
    fun3使用myclass對象的引用作為參數,這叫傳引用方式。在函數內部修改了
mc的成員變數a,由於前面說過,訪問一個對象和訪問該對象的引用,實際上是訪
問同一塊記憶體地區,因此這將直接修改myclass的成員變數a。

    從fun1和fun3的函數體也可看出,使用對象和使用對象的引用,在文法格式
上是一樣的。

    在fun1中c++將把實參的一個拷貝傳遞給形參。因此如果實參占記憶體很大,那
麼在參數傳遞中的系統開銷將很大。而在fun2和fun3中,無論是傳遞實參的指標
和實參的引用,都只傳遞實參的地址給形參,充其量也就是四個位元組,系統開銷
很小。

三。返回引用的函數

    引用還有一個很有用的特性:如果一個函數返回的是一個參考型別,那麼該
函數可以被當作左值使用。什麼是左值搞不懂先別管,只需瞭解:如果一個對象
或運算式可以放在賦值號的左邊,那麼這個對象和運算式就叫左值。
    舉一個雖然無用但很說明問題的例子:
例四:
        1。     int i;
        2。     int& f1(int&);
        3。     int  f2(int);
        4。     f1(i)=3;
        5。     f2(i)=4;

                int& f1(int&i)
                {
                   return i;
                }
   
                int f2(int i)
                {
                   return i;
                }
    試試編譯一下,你會發現第4句是對的,第5句是錯的。對這個例子而言,i的
引用被傳遞給了f1,然後f1把這個引用原樣返回,第4句的意義和i=3是一樣的。

    查了查書,引用的這個特性在重載操作符時用得比較多。但是我對重載操作
符還是稀裡糊塗,所以就舉不出例子了。
    強調一個小問題,看看如下代碼有何錯誤:

                int &f1();
                
                f1()=5;
                ...
                ...
                int &f1()
                {
                    int i;
                    int &ri=i;
                    return ri;
                }

    注意函數f1返回的引用ri是在函數體內聲明的,一旦函數返回後,超出了函
數範圍,ri所指向的記憶體地區,即對象i所佔據的記憶體地區就被收回了,再對這
片記憶體地區賦值會出錯的。

四。引用的轉換

    前面所舉的例子,引用的類型都是int類型,並且這些引用都被初始化為綁定
到int類型的對象。那麼我們設想是否可以聲明一個引用,它具有int類型,卻被
初始化綁定到一個float類型的對象?如下列代碼所示:
               float f;
               int &rv=f;
    結果證明這樣的轉換不能通過msvc++6.0的編譯。但是引用的轉換並非完全不
可能,事實上一個基類類型的引用可以被初始化綁定到衍生類別對象,只要滿足這
兩個條件:1,指定的基類是可訪問的。2,轉換是無二義性的。舉個例子:
例五:
          class A
          {
            public:
                int a;
          };
          class B:public A
          {
           public:
                int b;
          };
          A Oa;
          B Ob;
          A& mm=Ob;
          mm.a=3;

    我們有一個基類A和衍生類別B,並且有一個基類對象Oa和衍生類別對象Ob,我們
還聲明了一個引用mm,它具有基類類型但被綁定到衍生類別對象Ob上。由於我們的
這兩個類都很簡單,滿足那兩個條件,因此這段代碼是合法的。在這個例子中,
mm和衍生類別Ob中的基類子物件是共用一段記憶體單元的。所以,語句mm.a=3相當於
Ob.a=3,但是運算式mm.b卻是不合法的,因為基類子物件並不包括衍生類別的成員

五。總結

    最後把引用給總結一下:
1。對象和對象的引用在某種意義上是一個東西,訪問對象和訪問對象的引用其實
訪問的是同一塊記憶體區。
2。使用對象和使用對象的引用在文法格式上是一樣的。
3。引用必須初始化。
4。引用在初始化中被綁定到某個對象上後,將只能永遠綁定這個對象。
5。基類類型的引用可以被綁定到該基類的衍生類別對象,只要基類和衍生類別滿足上
文提到的兩個條件   。這時, 該引用其實是衍生類別對象中的基類子物件的引用

6。用傳遞引用的方式給函數傳遞一個對象的引用時,只傳遞了該對象的地址,系
統消耗較小。在函數體內訪問    形參,實際是訪問了這個作為實參的對象。
7。一個函數如果返回引用,那麼函數調用運算式可以作為左值。

六。其他
1。本文中的代碼在msvc++6.0中調實驗證過。
2。第四節“引用的轉換”中的例子:
               float f;
               int &rv=f;
   查看bc++3.1的資料,據說是合法的。此時編譯器產生了一個float類型的臨時
對象,引用rv被綁定到了這個臨時對象上,就是說,此時rv並不是f的引用。不知
道bc++3.1裡的這個特性有什麼用。
3。可以在msvc++6.0裡聲明這樣的引用:
               const int &rv=3;
   此時rv的值就是3,而且無法更改。這可能沒有有什麼用。因為如果我們要使
用一個符號來代表常數的話,有的是更常見的方法:
               #define rv 3
4。把第四節中的例子稍稍修改一下:
               float f;
               int &rv=(int&)f;
   這時就可以通過msvc++6.0的編譯了。此時rv被綁定到了f上,rv和f共用一片
儲存區。不過由於引用rv的類型是int,所以通過rv去訪問這片儲存區時,儲存區
的內容被解釋為整數;通過f去訪問這片儲存區時,儲存區的內容被解釋為實數。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.