深入分析C++引用

TuxedoLinux發表於2018-06-03

原文地址: http://www.codeproject.com/KB/cpp/References_in_c__.aspx

http://blog.chinaunix.net/uid/24517549/cid-169976-list-1.html

 

引言

      我選擇寫 C++ 中的引用是因為我感覺大多數人誤解了引用。而我之所以有這個感受是因為我主持過很多 C++ 的面試,並且我很少從面試者中得到關於 C++ 引用的正確答案。

       那麼 c++ 中引用到底意味這什麼呢?通常一個引用讓人想到是一個引用的變數的別名,而我討厭將 c++ 中引用定義為變數的別名。這篇文章中,我將盡量解釋清楚, c++ 中根本就沒有什麼叫做別名的東東。

 

背景

在 c/c++ 中,訪問一個變數只能通過兩種方式被訪問,傳遞,或者查詢。這兩種方式是:

1. 通過值 訪問 / 傳遞變數

2. 通過地址 訪問 / 傳遞變數 – 這種方法就是指標

 

       除此之外沒有第三種訪問和傳遞變數值的方法。引用變數也就是個指標變數,它也擁有記憶體空間。最關鍵的是引用是一種會被編譯器自動解引用的指標。很難相信麼?讓我們來看看吧。。。

 

下面是一段使用引用的簡單 c++ 程式碼

 

  1. #include   
  2. int main()  
  3. {  
  4.     int i = 10;   // A simple integer variable  
  5.     int &j = i;   // A Reference to the variable i  
  6.     j++;   // Incrementing j will increment both i and j.  
  7.     // check by printing values of i and j  
  8.     cout<<  i  <<  j  <
  9.     // Now try to print the address of both variables i and j  
  10.     cout<<  &i  <<  &j  <
  11.     // surprisingly both print the same address and make us feel that they are  
  12.     // alias to the same memory location.  
  13.     // In example below we will see what is the reality  
  14.     return 0;  
  15. }   


 

引用其實就是 c++ 中的常量指標。表示式   int &i = j; 將會被編譯器轉化成 int *const i = &j; 而引用之所以要初始化是因為 const 型別變數必須初始化,這個指標也必須有所指。下面我們再次聚焦到上面這段程式碼,並使用編譯器的那套語法將引用替換掉。

 


  1. #include   
  2. int main()  
  3. {  
  4.     int i = 10;            // A simple integer variable  
  5.     int *const j = &i;     // A Reference to the variable i  
  6.     (*j)++;                // Incrementing j. Since reference variables are   
  7.                           // automatically dereferenced by compiler  
  8.     // check by printing values of i and j  
  9.     cout<<  i  <<  *j  <
  10.     // A * is appended before j because it used to be reference variable  
  11.     // and it should get automatically dereferenced.  
  12.     return 0;  
  13. }  


 

    讀者一定很奇怪為什麼我上面這段程式碼會跳過列印地址這步。這裡需要一些解釋。因為引用變數時會被編譯器自動解引用的,那麼一個諸如   cout << &j << endl; 的語句,編譯器就會將其轉化成語句   cout << &*j << endl;   現在&* 會相互抵消,這句話變的毫無意義,而 cout 列印的 j 值就是 i 的地址,因為其定義語句為 int *const j = &i;

 

      所以語句 cout << &i << &j << endl; 變成了 cout << &i << &*j << endl; 這兩種情況都是列印輸出 i 的地址。這就是當我們列印普通變數和引用變數的時候會輸出相同地址的原因。

 

      下面給出一段複雜一些的程式碼,來看看引用在級聯 (cascading) 中是如何運作的。

 


  1. #include   
  2. int main()  
  3. {  
  4.     int i = 10; // A Simple Integer variable  
  5.     int &j = i; // A Reference to the variable  
  6.     // Now we can also create a reference to reference variable.   
  7.     int &k = j; // A reference to a reference variable  
  8.     // Similarly we can also create another reference to the reference variable k  
  9.     int &l = k; // A reference to a reference to a reference variable.  
  10.     // Now if we increment any one of them the effect will be visible on all the  
  11.     // variables.  
  12.     // First print original values  
  13.     // The print should be 10,10,10,10  
  14.     cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <
  15.     // increment variable j  
  16.     j++;   
  17.     // The print should be 11,11,11,11  
  18.     cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <
  19.     // increment variable k  
  20.     k++;  
  21.     // The print should be 12,12,12,12  
  22.     cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <
  23.     // increment variable l  
  24.     l++;  
  25.     // The print should be 13,13,13,13  
  26.     cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <
  27.     return 0;  
  28. }  


 

下面這段程式碼是將上面程式碼中的引用替換之後程式碼,也就是說明我們不依賴編譯器的自動替換功能,手動進行替換也能達到相同的目標。

 


  1. #include   
  2. int main()  
  3. {  
  4.     int i = 10;         // A Simple Integer variable  
  5.     int *const j = &i;     // A Reference to the variable  
  6.     // The variable j will hold the address of i  
  7.     // Now we can also create a reference to reference variable.   
  8.     int *const k = &*j;     // A reference to a reference variable  
  9.     // The variable k will also hold the address of i because j   
  10.     // is a reference variable and   
  11.     // it gets auto dereferenced. After & and * cancels each other   
  12.     // k will hold the value of  
  13.     // j which it nothing but address of i  
  14.     // Similarly we can also create another reference to the reference variable k  
  15.     int *const l = &*k;     // A reference to a reference to a reference variable.  
  16.     // The variable l will also hold address of i because k holds address of i after  
  17.     // & and * cancels each other.  
  18.     // so we have seen that all the reference variable will actually holds the same  
  19.     // variable address.  
  20.     // Now if we increment any one of them the effect will be visible on all the  
  21.     // variables.  
  22.     // First print original values. The reference variables will have * prefixed because   
  23.     // these variables gets automatically dereferenced.  
  24.     // The print should be 10,10,10,10  
  25.     cout<<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <
  26.     // increment variable j  
  27.     (*j)++;   
  28.     // The print should be 11,11,11,11  
  29.     cout<<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <
  30.     // increment variable k  
  31.     (*k)++;  
  32.     // The print should be 12,12,12,12  
  33.     cout<<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <
  34.     // increment variable l  
  35.     (*l)++;  
  36.     // The print should be 13,13,13,13  
  37.     cout  <<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <
  38.     return 0;  
  39. }  


 

         我們通過下面程式碼可以證明 c++ 的引用不是神馬別名,它也會佔用記憶體空間的。


  1. #include   
  2. class Test  
  3. {  
  4.     int &i;   // int *const i;  
  5.     int &j;   // int *const j;  
  6.     int &k;   // int *const k;   
  7. };  
  8. int main()  
  9. {      
  10.     // This will print 12 i.e. size of 3 pointers  
  11.     cout<<  "size of class Test = "  <<   sizeof(class Test)  <
  12.     return 0;  
  13. }  

相關文章