#include <iostream>
#include <string>
using namespace std;
void main(int argc,char* argv[])
{
int a=10;
int b=20;
int &rn=a;
cout<<rn<<"|"<<a<<endl;
cout<<&rn<<"|"<<&a<<endl;//c++中是無法取得引用的記憶體位址的,取引用的地址就是取目標的地址!
rn=b;//把引用指向另一個目標----變數b
cout<<&rn<<"|"<<&a<<"|"<<&b<<endl;
rn=100;//試圖改變b的值
cout<<a<<"|"<<b<<endl;//輸出修改後的結果
cin.get();
}
由於引用本身就是目標的一個別名,引用本身的地址是一個沒有意義的值,所以在c++中是無法取得引用的記憶體位址的。取引用的地址就是取目標的地址,c++本身就根本不提供擷取引用記憶體位址的方法。
引用一旦初始化,就不在能夠被指向其它的目標,雖然編譯不會出錯,但操作是不起作用的,實際上還是指向最先指向的目標。
上面代碼中的rn=b實際在電腦看來就是a=b,所以修改的還是a的值。
#include <iostream>
#include <string>
using namespace std;
void main(int argc,char* argv[])
{
int a=10;
void &rn=a;// 錯誤的,void即無類型的類型
int a[100];
int &ra[100]=a;//錯誤,不能聲明引用數組
cin.get();
}
上面的兩錯誤要記住引用的特性,void修飾是不能夠聲明引用的,引用是不能夠聲明數組的,即不能夠聲明引用數組。
下面我們要說一下,也是補充中最重要最需要掌握的內容,也是對傳統函數操作的記憶體狀態的一個補充學習。
下面我們來看一個例子:
#include <iostream>
#include <string>
using namespace std;
float c;
float test(float,float);
void main(int argc,char* argv[])
{
float pn=test(3.0f,1.2f);
cout<<pn;
cin.get();
}
float test(float a,float b)
{
c=a*b;
return c;
}
在上面的代碼中我們可能我們可能以為函數返回的就是c變數,呵呵。這麼想可能就錯了,普通情況下我們在函數內進行普通值返回的時候在記憶體棧空間內其實是自動產生了一個臨時變數temp,它是傳回值的一個副本一個copy,函數在return的時候其實是return的這個臨時產生的副本。
資料在記憶體中的情況如:
明確表示了副本領事變數的情況。
下面我們再來看一種情況,就是把傳回值賦給引用:
#include <iostream>
#include <string>
using namespace std;
float c;
float test(float,float);
void main(int argc,char* argv[])
{
float &pn=test(3.0f,1.2f);//警告:返回的將是臨時變數,pn引用將成為臨時變數的別名!
cout<<pn;
cin.get();
}
float test(float a,float b)
{
c=a*b;
return c;
}
float &pn=test(3.0f,1.2f);這句在bc中能夠編譯通過,因為bc擴充設定為臨時變數設定引用,那麼臨時變數的生命週期將和引用的生命週期一致,但在vc中卻不能通過編譯,因為一但test()執行過後臨時變數消失在棧空間內,這時候pn將成為一個沒有明確目標的引用,嚴重的時候會導致記憶體出錯。
它在記憶體中的情況見:
我們在圖中看到,由於函數仍然是普通方法返回,所以仍然會有一個副本臨時變數產生,只不過,這一次只是返回一個目標地址,在main中目標地址被賦予了引用pn。
下面我們再看一種情況,這是返回引用給變數的情況:
#include <iostream>
#include <string>
using namespace std;
float c;
float& test(float,float);
void main(int argc,char* argv[])
{
float pn=test(3.0f,1.2f);
cout<<pn;
cin.get();
}
float &test(float a,float b)
{
c=a*b;
return c;
}
這種返回引用給變數的情況下,在記憶體中,test()所在的棧空間內並沒有產生臨時變數,而是直接將全域變數c的值給了變數pn,這種方式是我們最為推薦的操作方式,因為不產生臨時變數直接賦值的方式可以節省記憶體空間提高效率,程式的可讀性也是比較好的。
它在記憶體中的情況見:
最後的一種情況是函數返回引用,並且發值賦給一個引用的情況:
#include <iostream>
#include <string>
using namespace std;
float c;
float& test(float,float);
void main(int argc,char* argv[])
{
float &pn=test(3.0f,1.2f);
cout<<pn;
cin.get();
}
float &test(float a,float b)
{
c=a*b;
return c;
}
這種情況同樣也不產生臨時變數,可讀和效能都很好,但有一點容易弄錯,就是當c是非main的局部變數或者是在堆記憶體中臨時開闢後來又被fee掉了以後的地區,這種情況和返回的指標是局部指標的後果一樣嚴重,會導致引用指向了一個不明確的地址,這種情況在記憶體中情況見:
由於這種情況存在範圍的問題,故我們推薦採用第三種方式處理。
接下來我們說幾個利用引用作為左值參與計算的例子,這一點一非常重要,對於理解返回引用的函數是非常有協助的。
#include <iostream>
#include <string>
using namespace std;
float c;
float& test(float,float);
void main(int argc,char* argv[])
{
float &pn=test(3.0f,1.2f);
cout<<pn<<endl;
test(3.0f,1.2f)=12.1;//把函數作左值進行計算!
cout<<pn;
cin.get();
}
float &test(float a,float b)
{
c=a*b;
return c;
}
通常來說函數是不能作為左值,因為引用可以做為左值,所以返回引用的函數自然也就可以作為左值來計算了。
在上面的代碼中:
float &pn=test(3.0f,1.2f);
進行到這裡的時候pn已經指向到了目標c的地址了。
接下來運行了
test(3.0f,1.2f)=12.1;
把函數作左值進行計算,這裡由於test是返回引用的函數,其實傳回值返回的地址就是c的地址,自然c的值就被修改成了12.1。