深入分析C++引用
原文地址: 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++ 程式碼
- #include
- int main()
- {
- int i = 10; // A simple integer variable
- int &j = i; // A Reference to the variable i
- j++; // Incrementing j will increment both i and j.
- // check by printing values of i and j
- cout<< i << j <
- // Now try to print the address of both variables i and j
- cout<< &i << &j <
- // surprisingly both print the same address and make us feel that they are
- // alias to the same memory location.
- // In example below we will see what is the reality
- return 0;
- }
引用其實就是 c++ 中的常量指標。表示式 int &i = j; 將會被編譯器轉化成 int *const i = &j; 而引用之所以要初始化是因為 const 型別變數必須初始化,這個指標也必須有所指。下面我們再次聚焦到上面這段程式碼,並使用編譯器的那套語法將引用替換掉。
- #include
- int main()
- {
- int i = 10; // A simple integer variable
- int *const j = &i; // A Reference to the variable i
- (*j)++; // Incrementing j. Since reference variables are
- // automatically dereferenced by compiler
- // check by printing values of i and j
- cout<< i << *j <
- // A * is appended before j because it used to be reference variable
- // and it should get automatically dereferenced.
- return 0;
- }
讀者一定很奇怪為什麼我上面這段程式碼會跳過列印地址這步。這裡需要一些解釋。因為引用變數時會被編譯器自動解引用的,那麼一個諸如 cout << &j << endl; 的語句,編譯器就會將其轉化成語句 cout << &*j << endl; 現在&* 會相互抵消,這句話變的毫無意義,而 cout 列印的 j 值就是 i 的地址,因為其定義語句為 int *const j = &i;
所以語句 cout << &i << &j << endl; 變成了 cout << &i << &*j << endl; 這兩種情況都是列印輸出 i 的地址。這就是當我們列印普通變數和引用變數的時候會輸出相同地址的原因。
下面給出一段複雜一些的程式碼,來看看引用在級聯 (cascading) 中是如何運作的。
- #include
- int main()
- {
- int i = 10; // A Simple Integer variable
- int &j = i; // A Reference to the variable
- // Now we can also create a reference to reference variable.
- int &k = j; // A reference to a reference variable
- // Similarly we can also create another reference to the reference variable k
- int &l = k; // A reference to a reference to a reference variable.
- // Now if we increment any one of them the effect will be visible on all the
- // variables.
- // First print original values
- // The print should be 10,10,10,10
- cout<< i << "," << j << "," << k << "," << l <
- // increment variable j
- j++;
- // The print should be 11,11,11,11
- cout<< i << "," << j << "," << k << "," << l <
- // increment variable k
- k++;
- // The print should be 12,12,12,12
- cout<< i << "," << j << "," << k << "," << l <
- // increment variable l
- l++;
- // The print should be 13,13,13,13
- cout<< i << "," << j << "," << k << "," << l <
- return 0;
- }
下面這段程式碼是將上面程式碼中的引用替換之後程式碼,也就是說明我們不依賴編譯器的自動替換功能,手動進行替換也能達到相同的目標。
- #include
- int main()
- {
- int i = 10; // A Simple Integer variable
- int *const j = &i; // A Reference to the variable
- // The variable j will hold the address of i
- // Now we can also create a reference to reference variable.
- int *const k = &*j; // A reference to a reference variable
- // The variable k will also hold the address of i because j
- // is a reference variable and
- // it gets auto dereferenced. After & and * cancels each other
- // k will hold the value of
- // j which it nothing but address of i
- // Similarly we can also create another reference to the reference variable k
- int *const l = &*k; // A reference to a reference to a reference variable.
- // The variable l will also hold address of i because k holds address of i after
- // & and * cancels each other.
- // so we have seen that all the reference variable will actually holds the same
- // variable address.
- // Now if we increment any one of them the effect will be visible on all the
- // variables.
- // First print original values. The reference variables will have * prefixed because
- // these variables gets automatically dereferenced.
- // The print should be 10,10,10,10
- cout<< i << "," << *j << "," << *k << "," << *l <
- // increment variable j
- (*j)++;
- // The print should be 11,11,11,11
- cout<< i << "," << *j << "," << *k << "," << *l <
- // increment variable k
- (*k)++;
- // The print should be 12,12,12,12
- cout<< i << "," << *j << "," << *k << "," << *l <
- // increment variable l
- (*l)++;
- // The print should be 13,13,13,13
- cout << i << "," << *j << "," << *k << "," << *l <
- return 0;
- }
我們通過下面程式碼可以證明 c++ 的引用不是神馬別名,它也會佔用記憶體空間的。
- #include
- class Test
- {
- int &i; // int *const i;
- int &j; // int *const j;
- int &k; // int *const k;
- };
- int main()
- {
- // This will print 12 i.e. size of 3 pointers
- cout<< "size of class Test = " << sizeof(class Test) <
- return 0;
- }
相關文章
- C++ 引用C++
- 【C++】引用C++
- 深入分析JVM中的物件及引用(十六)JVM物件
- Spring 迴圈引用(三)原始碼深入分析版Spring原始碼
- C++左值引用與右值引用C++
- C++ 右值引用和左值引用C++
- c++ 左值引用與右值引用C++
- C++右值引用C++
- 詳解C++引用C++
- c++筆記_引用C++筆記
- Python與C++引用分析PythonC++
- 119 C++中的引用&C++
- C++的引用技術C++
- 開心檔之C++ 引用C++
- C++引用型別詳解C++型別
- C++ 引用型別簡介C++型別
- C++引用全部知識點C++
- Rust引用自定義c/c++庫RustC++
- C++中指標與引用詳解C++指標
- 【C++】兩個類的相互引用C++
- C++ 左值引用和右值引用之間的轉換C++
- java的引用:用C++/C的引用和指標去理解JavaC++指標
- C++中的&引用符號全解C++符號
- C++ :引用計數(reference count) 實現C++
- c++ 類的函式引用 指標C++函式指標
- c++中指標和引用的區別?C++指標
- C++精進之路4:引用的用法C++
- 深入分析C++物件模型之移動建構函式C++物件模型函式
- c++指標傳遞與引用傳遞C++指標
- C++中const與指標、引用的總結C++指標
- C++與Rust引用外部符號的比較C++Rust符號
- 關於java的引用和c++的區別JavaC++
- C++ 學習筆記(3):引用和指標C++筆記指標
- c++之引用及記憶體分割槽模型C++記憶體模型
- C++智慧指標學習——小談引用計數C++指標
- C++ 只能指標迴圈引用簡單測試C++指標
- C++對C語言的擴充套件(1)--引用C++C語言套件
- C++名稱空間、標準輸入輸出、引用C++