最近看書,看到了引用,對其用法不是很了解。從各處匯總了一些知識,如下:
什么是引用
引用是某一變量(目標)的一個別名,對引用的操作與對變量直接操作完全一樣。引用的聲明方法:類型標識符 &引用名=目標變量名;
【例1】:
int a
int &ra=a; //定義引用ra, 它是變量a的引用,即別名
對引用的幾點說明
(1)&在此不是求地址運算,而是起標識作用。
(2)類型標識符是指目標變量的類型。
(3)聲明引用時,必須同時對其進行初始化。
(4)引用聲明完畢后,相當于目標變量名有兩個名稱,即該目標原名稱和引用名,且不能再把該引用名作為其他變量名的別名。
ra=1; 等價于 a=1;
(5)聲明一個引用,不是新定義了一個變量,它只表示該引用名是目標變量名的一個別名,它本身不是一種數(shù)據(jù)類型,因此引用本身不占存儲單元,系統(tǒng)也不給引用分配存儲單元。故:對引用求地址,就是對目標變量求地址。&ra與&a相等
(6)不能建立數(shù)組的引用。因為數(shù)組是一個由若干個元素所組成的集合,所以無法建立一個數(shù)組的別名。
引用的用途
引用的主要用途是為了描述函數(shù)的參數(shù)和返回值,特別是為了運算符的重載。
1、 引用作為參數(shù)
引用的一個重要作用就是作為函數(shù)的參數(shù)。以前的C語言中函數(shù)參數(shù)傳遞是值傳遞,如果有大塊數(shù)據(jù)作為參數(shù)傳遞的時候,采用的方案往往是指針,因為這樣可以避免將整塊數(shù)據(jù)全部壓棧,可以提高程序的效率。但是現(xiàn)在(C++中)又增加了一種同樣有效率的選擇(在某些特殊情況下又是必須的選擇),就是引用。
【例2】:
void swap(int &p1, int &p2) / /此處函數(shù)的形參p1, p2都是引用
{ int p; p=p1; p1=p2; p2=p; }
為在程序中調(diào)用該函數(shù),則相應(yīng)的主調(diào)函數(shù)的調(diào)用點處,直接以變量作為實參進行調(diào)用即可,而不需要實參變量有任何的特殊要求。如:對應(yīng)上面定義的swap函數(shù),相應(yīng)的主調(diào)函數(shù)可寫為:
【例3】:
main( )
{
int a,b;
cin>>a>>b; // 輸入a,b兩變量的值
swap(a,b); //直接以變量a和b作為實參調(diào)用swap函數(shù)
cout<<a<< ' ' <<b; //輸出結(jié)果
}
上述程序運行時,如果輸入數(shù)據(jù)10 20并回車后,則輸出結(jié)果為20 10。
由【例2】可看出:
(1)傳遞引用給函數(shù)與傳遞指針的效果是一樣的。這時,被調(diào)函數(shù)的形參就成為原來主調(diào)函數(shù)中的實參變量或?qū)ο蟮囊粋€別名來使用,所以在被調(diào)函數(shù)中對形參變量的操作就是對其相應(yīng)的目標對象(在主調(diào)函數(shù)中)的操作。
(2)使用引用傳遞函數(shù)的參數(shù),在內(nèi)存中并沒有產(chǎn)生實參的副本,它是直接對實參操作;而使用一般變量傳遞函數(shù)的參數(shù),當發(fā)生函數(shù)調(diào)用時,需要給形參分配存儲單元,形參變量是實參變量的副本;如果傳遞的是對象,還將調(diào)用拷貝構(gòu)造函數(shù)。因此,當參數(shù)傳遞的數(shù)據(jù)較大時,用引用比用一般變量傳遞參數(shù)的效率和所占空間都好。
(3)使用指針作為函數(shù)的參數(shù)雖然也能達到與使用引用的效果,但是,在被調(diào)函數(shù)中同樣要給形參分配存儲單元,且需要重復(fù)使用"*指針變量名"的形式進行運算,這很容易產(chǎn)生錯誤且程序的閱讀性較差;另一方面,在主調(diào)函數(shù)的調(diào)用點處,必須用變量的地址作為實參。而引用更容易使用,更清晰。
如果既要利用引用提高程序的效率,又要保護傳遞給函數(shù)的數(shù)據(jù)不在函數(shù)中被改變,就應(yīng)使用常引用。
2、 引用作為返回值
如果一個函數(shù)返回了引用,那么該函數(shù)的調(diào)用也可以被賦值。這里有一函數(shù),它擁有兩個引用參數(shù)并返回一個雙精度數(shù)的引用:
【例4】
double &max(double &d1,double &d2)
{
return d1>d2?d1:d2;
}
由于max()函數(shù)返回一個對雙精度數(shù)的引用,那么我們就可以用max() 來對其中較大的雙精度數(shù)加1:
max(x,y)+=1.0;
在Effecitve c++中指出,當你必須返回一個對象時不要試圖返回一個引用。這又是為什么呢?我看了一下它的解釋
考慮一個代表有理數(shù)的類,包含一個將兩個有理數(shù)相乘的函數(shù):
class Rational {
public:
Rational(int numerator = 0, // see Item 24 for why this
int denominator = 1); // ctor isn't declared explicit
...
private:
int n, d; // numerator and denominator
friend const Rational // see Item 3 for why the
operator*(const Rational& lhs, // return type is cons
const Rational& rhs);
};
operator* 的這個版本以傳值方式返回它的結(jié)果,而且如果你沒有擔(dān)心那個對象的構(gòu)造和析構(gòu)的代價,你就是在推卸你的專業(yè)職責(zé)。如果你不是迫不得已,你不應(yīng)該為這樣的一個對象付出成本。所以問題就在這里:你是迫不得已嗎?
哦,如果你能用返回一個引用來作為代替,你就不是迫不得已。但是,請記住一個引用僅僅是一個名字,一個實際存在的對象的名字。無論何時只要你看到一個引用的聲明,你應(yīng)該立刻問自己它是什么東西的另一個名字,因為它必定是某物的另一個名字。在這個 operator* 的情況下,如果函數(shù)返回一個引用,它必須返回某個已存在的而且其中包含兩個對象相乘的產(chǎn)物的 Rational 對象的引用。
當然沒有什么理由期望這樣一個對象在調(diào)用 operator* 之前就存在。也就是說,如果你有
Rational a(1, 2); // a = 1/2
Rational b(3, 5); // b = 3/5
Rational c = a * b; // c should be 3/10
似乎沒有理由期望那里碰巧已經(jīng)存在一個值為十分之三的有理數(shù)。不是這樣的,如果 operator* 返回這樣一個數(shù)的引用,它必須自己創(chuàng)建那個數(shù)字對象。
一個函數(shù)創(chuàng)建一個新對象僅有兩種方法:在棧上或者在堆上。棧上的生成物通過定義一個局部變量而生成。使用這個策略,你可以用這種方法試寫 operator*:
const Rational& operator*(const Rational& lhs, // warning! bad code!
const Rational& rhs)
{
Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
你可以立即否決這種方法,因為你的目標是避免調(diào)用構(gòu)造函數(shù),而 result 正像任何其它對象一樣必須被構(gòu)造。一個更嚴重的問題是這個函數(shù)返回一個引向 result 的引用,但是 result 是一個局部對象,而局部對象在函數(shù)退出時被銷毀。那么,這個 operator* 的版本不會返回引向一個 Rational 的引用——它返回引向一個前 Rational;一個曾經(jīng)的 Rational;一個空洞的、惡臭的、腐敗的,從前是一個 Rational 但永不再是的尸體的引用,因為它已經(jīng)被銷毀了。任何調(diào)用者甚至于沒有來得及匆匆看一眼這個函數(shù)的返回值就立刻進入了未定義行為的領(lǐng)地。這是事實,任何返回一個引向局部變量的引用的函數(shù)都是錯誤的。(對于任何返回一個指向局部變量的指針的函數(shù)同樣成立。)
那么,讓我們考慮一下在堆上構(gòu)造一個對象并返回引向它的引用的可能性。基于堆的對象通過使用 new 而開始存在,所以你可以像這樣寫一個基于堆的 operator*:
const Rational& operator*(const Rational& lhs, // warning! more bad
const Rational& rhs) // code!
{
Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *result;
}
哦,你還是必須要付出一個構(gòu)造函數(shù)調(diào)用的成本,因為通過 new 分配的內(nèi)存要通過調(diào)用一個適當?shù)臉?gòu)造函數(shù)進行初始化,但是現(xiàn)在你有另一個問題:誰是刪除你用 new 做出來的對象的合適人選?
即使調(diào)用者盡職盡責(zé)且一心向善,它們也不太可能是用這樣的方案來合理地預(yù)防泄漏:
Rational w, x, y, z;
w = x * y * z;
這里,在同一個語句中有兩個 operator* 的調(diào)用,因此 new 被使用了兩次,這兩次都需要使用 delete 來銷毀。但是 operator* 的客戶沒有合理的辦法進行那些調(diào)用,因為他們沒有合理的辦法取得隱藏在通過調(diào)用 operator* 返回的引用后面的指針。這是一個早已注定的資源泄漏。
似乎明白了一些:當你必須返回一個對象時不要試圖返回一個引用。對于局部變量,返回其引用會造成錯誤。為了避免錯誤,我們應(yīng)盡量不返回引用。
3、 常引用
const引用是指向const對象的引用:
const int ival = 1024;
const int &refVal = ival; // ok: both reference and object are const
int &ref2 = ival; // error: non const reference to a const object
常引用聲明方式:const 類型標識符 &引用名=目標變量名;
用這種方式聲明的引用,不能通過引用對目標變量的值進行修改,從而使引用的目標成為const,達到了引用的安全性。
【例5】:
int a ;
const int &ra = a;
ra=1; // 錯誤
a=1; // 正確
這不光是讓代碼更健壯,也有些其它方面的需要。
【例6】:假設(shè)有如下函數(shù)聲明:
string foo( );
void bar(string & s);
那么下面的表達式將是非法的:
bar(foo( ))
bar("hello world");
原因在于foo( )和"hello world"串都會產(chǎn)生一個臨時對象,而在C++中,這些臨時對象都是const類型的。因此上面的表達式就是試圖將一個const類型的對象轉(zhuǎn)換為非const類型,這是非法的。
引用型參數(shù)應(yīng)該在能被定義為const的情況下,盡量定義為const 。
Const 引用與非const引用的區(qū)別
非const引用只能綁定到與該引用同類型的對象。
const引用則可以綁定到不同但相關(guān)的類型的對象或綁定到左值。
指針和引用的差別
1. 非空的差別任何情況下都不能使用指向空值的引用.一個引用必須總是指向某個對象. 不存在的指向空值的引用這個事實意味著使用引用的代碼效率比使用指針要高.
2. 合法性區(qū)別在使用引用之前不需要測試他的合法性.指針必須測試.
3. 可修改區(qū)別 指針可以被重新賦值給另一個不同的對象.但是引用總是指向在初始化的時候被制定的對象,以后不能改變.但是指定的對象其內(nèi)容可以改變. 應(yīng)該使用指針的情況: 可能存在不指向任何對象的可能性 需要在不同的時刻指向不同的對象(此時,你能夠改變指針的指向) 應(yīng)該使用引用的情況: 如果總是指向一個對象并且一旦指向一個對象后就不會改變指向,使用此時應(yīng)使用引用。
要首先好好理解指針和引用的區(qū)別
指針與引用看上去完全不同(指針用操作符’*’和’->’,引用使用操作符’.’),但是它們似乎有相同的功能。指針與引用都是讓你間接引用其他對象。你如何決定在什么時候使用指針,在什么時候使用引用呢?
總的來說,在以下情況下你應(yīng)該使用指針,一是你考慮到存在不指向任何對象的可能(在這種情況下,你能夠設(shè)置指針為空),二是你需要能夠在不同的時刻指向不同的對象(在這種情況下,你能改變指針的指向)。如果總是指向一個對象并且一旦指向一個對象后就不會改變指向,那么你應(yīng)該使用引用。
還有一種情況,就是當你重載某個操作符時,你應(yīng)該使用引用。最普通的例子是操作符[]。這個操作符典型的用法是返回一個目標對象,其能被賦值。
vector<int> v(10); //建立整形向量(vector),大小為10
//向量是一個在標準C庫中的一個模板 [Page]
v[5] = 10; // 這個被賦值的目標對象就是操作符[]返回的值
如果操作符[]返回一個指針,那么后一個語句就得這樣寫:
*v[5] = 10;
但是這樣會使得v看上去象是一個向量指針。因此你會選擇讓操作符返回一個引用
當你知道你必須指向一個對象并且不想改變其指向時,或者在重載操作符并為防止不必要的語義誤解時,你不應(yīng)該使用指針。而在除此之外的其他情況下,則應(yīng)使用指針 。
posted on 2008-10-12 21:18
Sandy 閱讀(254)
評論(0) 編輯 收藏 引用