引用(Reference)是C++語言相對於C語言的又一個擴充,類似於指針,只是在聲明的時候用&取代了*。引用可以看做是被引用對象的一個別名,在聲明引用時,必須同時對其進行初始化。引用的聲明方法如下:
類型標識符 &引用名 = 被引用對象
[例1]C++引用示例:
int a = 10;
int &b = a;
cout<<a<<" "<<b<<endl;
cout<<&a<<" "<<&b<<endl;
在本例中,變量b就是變量a的引用,程序運行結果如下:
10 10
0018FDB4 0018FDB4
從這段程序中我們可以看出變量a和變量b都是指向同一地址的,也即變量b是變量a的另一個名字,也可以理解爲0018FDB4空間擁有兩個名字:a和b。由於引用和原始變量都是指向同一地址的,因此通過引用也可以修改原始變量中所存儲的變量值,如例2所示,最終程序運行結果是輸出兩個20,可見原始變量a的值已經被引用變量b修改。
[例2]通過引用修改原始變量中的值:
int a = 10;
int &b = a;
b = 20;
cout<<a<<" "<<b<<endl;
如果我們不希望通過引用來改變原始變量的值時,我們可以按照如下的方式聲明引用:
const 類型標識符 & 引用名 = 被引用的變量名
這種引用方式成爲常引用。如例3所示,我們聲明b爲a的常引用,之後嘗試通過b來修改a變量的值,結果編譯報錯。雖然常引用無法修改原始變量的值,但是我們仍然可以通過原始變量自身來修改原始變量的值,如例3中,我們用a=20;語句將a變量的值由10修改爲20,這是沒有語法問題的。
[例3]不能通過常引用來修改原始值:
int a = 10;
const int &b = a;
b = 20; //compile error
a = 20;
通過例2,我們可以知道通過引用我們可以修改原始變量的值,引用的這一特性使得它用於函數傳遞參數或函數返回值時非常有用。
1) 函數引用參數
如果我們在聲明或定義函數的時候將函數的形參指定爲引用,則在調用該函數時會將實參直接傳遞給形參,而不是將實參的拷貝傳遞給形參。如此一來,如果在函數體中修改了該參數,則實參的值也會被修改。這跟函數的普通傳值調用還是有區別的。
[例3]函數的引用傳值:
#include<iostream>
using namespace std;
void swap(int &a, int &b);
int main()
{
int num1 = 10;
int num2 = 20;
cout<<num1<<" "<<num2<<endl;
swap(num1, num2);
cout<<num1<<" "<<num2<<endl;
return 0;
}
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
運行結果:
10 20
20 10
在本例中我們將swap函數的形參聲明爲引用,在調用swap函數的時候程序是將變量num1和num2直接傳遞給形參的,其中a是num1的別名,b是num2的別名,在swap函數體中交換變量a和變量b的值,也就相當於直接交換變量num1和變量num2的值了,因此程序最後num1=20,num2=10。
2) 函數引用返回值
在C++中非void型函數需要返回一個返回值,類似函數形參,我們同樣可以將函數的返回值聲明爲引用。普通的函數返回值是通過傳值返回,即將關鍵字return後面緊接的表達式運算結果或變量拷貝到一個臨時存儲空間中,然後函數調用者從臨時存儲空間中取到函數返回值,如例4所示。
[例4]函數的普通返回值:
#include<iostream>
using namespace std;
int valplus(int &a);
int main()
{
int num1 = 10;
int num2;
num2 = valplus(num1);
cout<<num1<<" "<<num2<<endl;
return 0;
}
int valplus(int &a)
{
a = a + 5;
return a;
}
在例4中,valplus函數採用的是普通的傳值返回,也即將變量a的結果加上5之後,將結果拷貝到一個臨時存儲空間,然後再從臨時存儲空間拷貝給num2變量。 當我們將函數返回值聲明爲引用的形式時,如例5所示。雖然例5運行結果和例4是相同的,但例5中的valplus函數在將a變量加上5之後,其運算結果是直接拷貝給num2的,中間沒有經過拷貝給臨時空間,再從臨時存儲空間中拷貝出來的這麼一個過程。這就是普通的傳值返回和引用返回的區別。
[例5]函數的引用返回值:
#include<iostream>
using namespace std;
int & valplus(int &a);
int main()
{
int num1 = 10;
int num2;
num2 = valplus(num1);
cout<<num1<<" "<<num2<<endl;
return 0;
}
int & valplus(int &a)
{
a = a + 5;
return a;
}
此外,我們還需要注意一個小問題。如果我們將例5中的valplus函數定義成例6中所示的形式,那麼這段程序就會產生一個問題,變量b的作用域僅在這個valplus函數體內部,當函數調用完成,b變量就會被銷燬。而此時我們若將b變量的值通過引用返回拷貝給變量num2的時候,有可能會出現在拷貝之前b變量已經被銷燬,從而導致num2變量獲取不到返回值。雖然這種情況在一些編譯器中並沒有發生,但是我們在設計程序的時候也是應該儘量避免這一點的。
在例4和例5中,我們就是爲了避免這一點才採用的引用傳遞參數。普通的傳值返回則不存在這樣的問題,因爲編譯器會將返回值拷貝到臨時存儲空間後再去銷燬b變量的。
[例6]一個可能獲取不到返回值的例子:
int & valplus(int a)
{
int b = a+5;
return b;
}