引用: 就是某一變量(目標(biāo))的一個別名, 對引用的操作與對變量直接操作完全一樣. 并不會為引用在內(nèi)存中分配內(nèi)存. 引用的地址與變量的地址是一致的.
1. 引用的定義
- 類型標(biāo)識符 &引用名=目標(biāo)變量名见咒;
int a = 10;
int &b = a;
-
&
在此不是求地址運(yùn)算符旗芬,而是起標(biāo)識作用公壤。 - 聲明引用時感混,必須同時對其進(jìn)行初始化
- 類型標(biāo)識符是指目標(biāo)變量的類型.
- 引用聲明完畢后,相當(dāng)于目標(biāo)變量有兩個名稱即該目標(biāo)原名稱和引用名脖镀,且不能再把該引用名作為其他變量名的別名.
- 聲明一個引用公给,不是新定義了一個變量基协,它只表示該引用名是目標(biāo)變量名的一個別名嗅蔬,它本身不是一種數(shù)據(jù)類型剑按,因此引用本身不占存儲單元,系統(tǒng)也不給引用分配存儲單元购城。故:對引用求地址吕座,就是對目標(biāo)變量求地址虐译。&b與&a相等
- 不能建立數(shù)組的引用瘪板。因?yàn)閿?shù)組是一個由若干個元素所組成的集合,所以無法建立一個數(shù)組的別名.
2. 引用的使用場景
引用的一個重要作用就是作為函數(shù)的參數(shù), 在C語言中, 函數(shù)參數(shù)的傳遞是值傳遞, 如果有大數(shù)據(jù)作為參數(shù)傳遞時, 采用的方案是指針, 這樣可以避免將整塊數(shù)據(jù)全部壓棧, 可以提高效率. 但是在C++中, 可以使用引用來達(dá)到相同的效率.
例: 在C中, 將兩個數(shù)進(jìn)行交換, 需要傳遞地址. 如下所示
//引用的用處1
//在傳統(tǒng)的交換值的過程中, 需要將地址傳遞到函數(shù)中例如.
void swap1(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 10;
int b = 20;
cout<<"a="<<a<<",b="<<b<<endl;
swap1(&a, &b);
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
輸出內(nèi)容
a=10,b=20
a=20,b=10
那么使用引用也能達(dá)到一樣的效果.代碼如下
void swap2(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
cout<<"a="<<a<<",b="<<b<<endl;
swap2(a, b);
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
輸出結(jié)果與上面一致. 所以 傳遞引用給函數(shù)和傳遞指針給函數(shù)的效果是一樣的.
這時, 被調(diào)用函數(shù)的形參就成為原來主函數(shù)中實(shí)參變量或?qū)ο蟮囊粋€別名來使用. 所以在被調(diào)用函數(shù)中對形參變量的操作就是對其相應(yīng)的目標(biāo)對象操作.
使用引用傳遞函數(shù)的參數(shù), 在內(nèi)存中并沒有產(chǎn)生實(shí)參的副本, 它是直接對實(shí)參進(jìn)行操作. 而一般變量傳遞函數(shù)的參數(shù), 當(dāng)發(fā)生函數(shù)調(diào)用時, 需要給形參分配內(nèi)存空間. 如果傳遞的是對象, 還需要將調(diào)用拷貝構(gòu)造函數(shù). 因此,當(dāng)參數(shù)傳遞的數(shù)據(jù)較大時, 用引用比一般變量傳遞參數(shù)的效率和所占空間都較好.
使用指針作為函數(shù)的參數(shù), 雖然能達(dá)到同樣的效果, 但是在被調(diào)用函數(shù)中同樣要給形參分配內(nèi)存, 且需要重復(fù)使用 *
指針變量名的形式進(jìn)行運(yùn)算, 容易產(chǎn)生錯誤, 可讀性差. 引用就較為清晰.
3. 常引用
如果要利用引用提高程序的效率, 又要保護(hù)傳遞給函數(shù)的數(shù)據(jù)不再函數(shù)內(nèi)被改變, 那么就要使用到常引用了.
常引用的聲明方式為: const 類型標(biāo)識符 & 引用名 = 目標(biāo)變量名
用這種方式聲明的引用, 不能通過引用對目標(biāo)變量的值進(jìn)行修改, 從而使引用的目標(biāo)成為const, 達(dá)到了引用的安全性.
例1:
void main(){
int a=1;
int &b=a;
b=2;
cout<<"a="<<a<<endl;//2
int c=1;
const int &d=c;
// d=2;//編譯錯誤 error C2166: l-value specifies const object
c=2;//正確
11 }
例2:
有以下兩個函數(shù)聲明
string foo();
void bar(string &s);
那么下面的兩個表達(dá)式是錯誤的.
bar(fool());
bar("hello");
原因在于傳入的foo()
和 hello
都會產(chǎn)生一個臨時對象, 而在C++中, 臨時對象都是 const
類型的. 因此上面的表達(dá)式就是試圖將一個 const
類型的對象轉(zhuǎn)換為非 const
類型.
注意: 引用型參數(shù)應(yīng)該在能被定義為 const
的情況下, 盡量定義為 const
4. 引用作為返回值.
要以引用返回函數(shù)值, 則函數(shù)定義時要按以下格式.
類型標(biāo)識符 &函數(shù)名(形參及類型說明) { 函數(shù)體 }
- 以引用返回函數(shù)值, 定義函數(shù)時, 需要在函數(shù)名前面加
&
- 用引用返回一個函數(shù)值的最大好處是, 在內(nèi)存中不會產(chǎn)生被返回值的副本.
例:
#include <iostream>
using namespace std;
int func1(int a) {
int temp = a * a * 10;
return temp;
}
int temp;
int & func2(int a) {
temp = a * a * 20;
//直接返回temp, 等價(jià)與
//int &b = temp;
//return b;
return temp;
}
int main() {
//第一種情況, 系統(tǒng)會生成要返回值的副本,即臨時變量.
int r1 = func1(2);
cout << "r1=" << r1 << endl;
//情況2, 系統(tǒng)不會產(chǎn)生返回值的副本
int r2 = func2(5);
cout << "r2=" << r2 << endl;
return 0;
}
引用作為返回值, 必須遵守以下規(guī)則.
- 不能返回局部變量的引用. 主要原因是局部變量會在函數(shù)返回后小會, 因此, 被返回的引用就成為了 "無所指" 的引用. 程序會進(jìn)入未知狀態(tài).
-
不能返回函數(shù)內(nèi)部使用new分配的內(nèi)存的引用. 雖然不存在局部變量的被動銷毀問題, 可對于這種情況, 會面臨其他尷尬的局面. 例如被函數(shù)返回的引用只是作為一個臨時變量出現(xiàn), 而沒有被賦予一個實(shí)際的變量, 那么這個引用所指向的空間(由new分配)就無法釋放, 造成
memory leak
. - 可以返回類成員的引用, 但最好是 const. 主要原因是當(dāng)對象的屬性是與某種業(yè)務(wù)規(guī)則相關(guān)聯(lián)的時候, 其賦值常常與某些其他屬性或者對象的狀態(tài)有關(guān), 因此有必要將賦值操作封裝在一個業(yè)務(wù)規(guī)則中. 如果其他對象可以獲得該屬性的非常量引用/或指針, 那么該屬性的單純賦值就會破壞業(yè)務(wù)規(guī)則的完整性.
總結(jié)
- 在引用的使用中, 單純給某個變量取個別名是毫無意義的, 引用的目的主要用于函數(shù)參數(shù)傳遞中, 解決大塊數(shù)據(jù)或?qū)ο蟮膫鬟f效率和空間不如意的問題.
- 用引用傳遞函數(shù)的參數(shù), 能保證參數(shù)傳遞中不產(chǎn)生副本, 提高傳遞的效率, 且通過 const 的使用, 保證了引用傳遞的安全性
- 引用與指針的區(qū)別是,指針通過某個指針變量指向一個對象后, 對它所指向的變量間接操作, 程序中使用指針, 可讀性差. 而引用本身就是目標(biāo)變量的別名, 對引用的操作就是對目標(biāo)變量的操作.