C++引用(Reference)

引用(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;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章