C++中的新特性:引用
先放一個例子
swap函式對比(作用,交換兩個數的數值)
void swap(int *a, int *b)//使用指標
{
int temp = *a;
*a = *b;
*b = temp;
return ;
}
void swap(int &a, int &b)//使用引用
{
int temp = a;
a = b;
b = temp;
return ;
}
什麼是引用
引用,就是一個變數的別名
再來一個例子
int a = 2;
int & ra = a;
這裡,我們定義ra,它是變數a的引用
下面會用上面定義的變數
放幾個引用的用法
引用玩法1
int a = 2;
int & ra = a;
ra = 3 //!!
如果使用了上面那一條語句會怎樣呢?
實際上
此時ra == 3 並且 a == 3
沒有動a啊,a為什麼會變啊
複習引用的定義
引用就是某一變數的別名
也就是說,變數ra 就是 變數a
我對ra變數執行的任何操作
等同於對a變數執行的任何操作
反之亦然
其實很好理解原理
變數a的地址和變數ra的地址是相同的
(可以自己在編譯器上試一試)
引用玩法2
還是一個例子
void function(int & r)
{
r = 6 ;
}
int main()
{
int a = 2;
function (a);
cout << a;
}
將a作為形參傳入函式
最後會輸出多少呢?
????
實際上,會輸出6
也就是說,a的值被修改為6
不是隻有傳入指標才會修改它的值的嗎?
再來複習一下
引用也就是說是變數的別名
還有原理
引用變數和原變數地址是相同的
在函式宣告中
void function(int & r)
定義了一個引用r
也就是說,r變數將會是傳入實參的引用
因此
我們對r怎樣
也就是對實際傳入變數怎樣
再來看回開頭的例子
相信大家應該都懂了
關於概念
按值傳遞
(就是直接傳數)
按址傳遞
(就是傳入地址指標)
按引用傳遞
(就和前面講到的一樣)
為什麼用引用
1. 好打,好看
自行對比
什麼時候不小心打少了個*號的時候
什麼時候*號和++優先順序搞錯的時候
(*a++ 的理解!!)
……
就會知道引用的方便了。
void swap(int *a, int *b)//使用指標
{
int temp = *a;
*a = *b;
*b = temp;
return ;
}
void swap(int &a, int &b)//使用引用
{
int temp = a;
a = b;
b = temp;
return ;
}
2. 效率高
這裡會扯到一個是否生成臨時變數的問題
- 按值傳遞
按值傳遞的最大優點就是在函式中無論怎麼修改變數
都不會改變傳入變數的值
原因便在於按值傳遞中函式內的是臨時生成的變數
和傳入的變數完全沒有關係
- 生成臨時變數如何影響效率
也許生成一個int變數效率差別不大
但是如果是傳入一個有著好幾個陣列的結構體
或者是一個龐大的類物件
計算機宣告這些的臨時變數的時候
將會耗費不少計算資源與儲存空間
這個時候,把形參定義為引用便顯得尤為重要
引用可以同時具有引用優點和按值傳遞的優點
按值傳遞的最大優點就是不怕傳入的變數被修改
引用可以嗎??
我們可以這樣子定義來避免變數被改動
void function(const int & a);
通過把a定義為常值引用(加一個const)
我們可以在兼顧引用效率高優點的同時
避免不小心被修改
若是a的值被修改編譯器會馬上報錯方便修改
關於引用的幾點注意事項
1. 函式形參有引用
以void function ( int & r)
為例
我們可以這樣子呼叫嗎?
int a = 2;
function(a+2);
function(4);
前面提到
引用的原理便是記憶體地址相同
可是,這裡的(a+2)有地址嗎?
可是,這裡的4有地址嗎?
我們可以這樣用嗎?
事實上,這裡講的引用的全名是左值引用
左值:能夠放在賦值語句左邊的東東
(也就是可被賦值,有自己的記憶體地址)
能夠進行左值引用的一定前提是左值!!
a+2 ,4 明顯不能夠放在賦值語句左邊
(a+2 = 5? 4 = 2?)
因此此時編譯器會報錯
這裡有例外:const引用
如果是void function (const int & r)
那麼這個時候我
function(a+2)不會出錯
並且,會像按值傳遞那樣生成臨時變數
下面有一個總結
int x;
int f ( int & n);
f(x);
f(x+2);// error
f(10);//error , too.
int f2 (const int & n);
f2(x);
f2(x+2);// temporary variable
f2(10);// temporary variable
關於返回引用
// 讀取一個引用,返回一個引用
int & function (int & a)
{
int b = 2;
a = a + b;
return a;
//返回一個引用
}
這裡有幾個比較容易出錯的地方
1. 我可以返回b嗎?
再複習一遍原理
引用變數和原變數記憶體地址一樣!
如果這個程式改成
int & function (int & a)
{
int b = 2;
b = a + b;
return b;
//返回一個引用
}
將會報錯!!
b 是一個區域性變數
區域性變數在程式碼塊內可見
當退出程式碼塊時自動銷燬!
原記憶體地址的內容將會消失
假設我int一個變數c
int a = 2;
c = function(a)
變數c的地址將會與函式中臨時建立的區域性變數b相同
但是函式一旦結束
該地址所對應的記憶體塊將會自動釋放!!
此時變數c內的內容將不可訪問(或者是垃圾資料)
返回引用(是個左值)
// 讀取一個引用,返回一個引用
int & function (int & a)
{
int b = 2;
a = a + b;
return a;
//返回一個引用
}
對這個函式,我們有一個比較奇葩的用法
function (a) = 3;
原理其實很簡單
因為函式返回值是個左值
所以可以放在賦值語句的左邊
那這條語句到底做了什麼呢?
1. 函式function接受變數a,
2. 函式體內,返回變數a的引用
3. 對變數a的引用,賦值給3;
所以最終,就是a = 3;
如果怕自己搞錯
寫出這樣的程式碼
if ( (function(a) = 4 );
(是不會報錯的!)
可以把函式宣告改成這樣
const int & function(int & a);
還有一種奇葩用法
int a = 2;
const int & r = a;
這裡,a和r實際上是同一個變數
(引用的定義?變數別名)
可是,同一個變數,
我用a就可以賦值,就可以修改
我用r就不能夠賦值和修改
(因為用了const)
關於指標,引用,傳值的選擇
- 如果向函式中傳遞陣列,只能用指標
- 如果需要修改傳入的變數,用引用和指標(引用更好)
- 如果傳遞比較大塊的東東,推薦引用或指標(當然引用更好)
- 如果怕傳入的變數被修改,請用const限制
- 沒有如果了
總結
- 引用必須是左值
- 引用可以提高效率
- 不能反悔區域性變數的引用
- 避免引用被修改可以用const來限制
- 陣列不能引用!對於陣列只能使用指標
- debug的時候你會有更多的總結
一些比較高階的東西
- 對於類的引用,既可以引用該類
也可以引用該基類的派生類
(這是實現多型的基礎) - c++11中還有右值引用