引用,簡單粗暴的解釋叫做別名,簡單粗暴的例子就是,我是熊叫大雄,但是很多時候別人不叫我熊叫大雄,會叫我大雄,粵語地區朋友愛叫我阿雄,有人叫我雄,所以,熊叫大雄這個變量的值是我,雄、大雄、阿雄是熊叫大雄的別名,通過別名叫到的值還是我,暈了沒?
這一篇主要講引用做函數參數,引用還有其它內容,不過我覺得做函數參數比較常用且好理解一點,
1、創建引用
引用的創建形式有點類似指針,但是又有不同,一般這樣創建引用:
int dNum1 = 1;
const int dNum2 = 1;
int &dNum11 = dNum1; // OK
int &dNum111; // ERROR
int &dNum1111 = 1; // ERROR
int &dNum21 = dNum2; // ERROR
上面這兩句已經表明,引用的創建的時候就必須初始化的,也就是創建一個引用的時候不能夠先說有個別名再說這個別名是誰的,必須是先有變量再有該變量引用,而且這個變量還不能是常量,否則也錯誤
在《C++ Primer Plus》一書有個說法是引用更接近const指針的偽裝,例如聲明引用的時候就有點像是這樣:
int &dNum11 = dNum1;
==>
int * const dNum11 = &dNum1
所以,引用一旦聲明之后就將與初始化的變量長伴一生,一生雙存,一死雙亡,一變俱變。
2、引用做函數參數
我覺得這個是比較能體現引用價值的地方之一。我們知道,在使用帶參函數的時候,我們需要考慮的是參數的傳入是否是變量本身,因為函數中有些操作可能是要對原變量進行改變的,之前C語言的做法是用指針來傳遞原變量地址,這樣的好處是如果需要將改變傳回或者改變原變量則不需要返回值,可以通過傳入指針直接改變。那C++提供了引用的新特性,所以修改傳入參數的時候就可以這樣用了:
void suibianla(int &dInput)
{
dInput++;
}
int main(int argc, char* argv[])
{
int dNum1 = 1;
const int dNum2 = 1;
int &dNum11 = dNum1;
std::cout << dNum1 << std::endl;
std::cout << dNum11 << std::endl;
suibianla(dNum1);
std::cout << dNum1 << std::endl;
std::cout << dNum11 << std::endl;
system("pause");
return 0;
}
這種方式跟指針做參數的方式有點類似,但是跟指針參數還是有區別的。
引用做參數的一些特點:
1、傳遞引用是傳遞原變量,不需要做變量拷貝,普通的變量做函數參數的時候會開辟內存拷貝數值,而傳遞引用則不需要開辟內存;
2、傳遞引用限制比傳遞普通函數嚴格,比如:
void suiBianLa(int dInput)
{
std::cout << "dInput 地址 " << &dInput << std::endl;
dInput++;
}
void aiZuoNiJiuZuoNi(int &dInput)
{
std::cout << "dInput 地址 " << &dInput << std::endl;
dInput++;
}
suiBianLa(dNum1 + 1); // OK
aiZuoNiJiuZuoNi(dNum1 + 1); // ERROR
aiZuoNiJiuZuoNi()的參數是引用,suiBianLa()的參數是普通變量,這個時候aiZuoNiJiuZuoNi()的使用是會報錯的:
error C2664: “void aiZuoNiJiuZuoNi(int &)”: 無法將參數 1 從“int”轉換為“int &”
3、如果既想用引用,又不想原始變量被改變,那么就用常量引用:
void aiZuoNi(const int &dInput)
{
std::cout << "dInput 地址 " << &dInput << std::endl;
dInput++; // ERROR
}
因為是常量引用,所以不能對輸入引用變量做改變的操作。
4、僅當使用常量引用的時候,如果輸入參數與引用參數不匹配,才會生成臨時變量,這種不匹配體現為:
(1)、實參的類型正確,但不是左值;
(2)、實參的類型不正確,但是可以轉換為正確的類型。
有點不好理解,左值是啥?簡單粗暴的說就是變量、數組元素、結構成員、引用和接觸引用的指針等,非左值有字面常量、包含多項式的表達式等。
void aiZuoNi(const float &fInput)
{
std::cout << "fInput地址 " << &fInput<< std::endl;
}
long lNum = 1L;
float fNum = 1L;
aiZuoNi(lNum); // lNum是long型,fInput是臨時變量
aiZuoNi(fNum); // fInput是fNum,沒有臨時變量
aiZuoNi(2.0); // 2.0常量,有臨時變量
aiZuoNi(fNum + 1); // fNum + 1是表達式,有臨時變量
同時注意到,aiZuoNi(fNum + 1)在非常量引用的時候是不行的,在常量引用下是可以的。
void aiZuoNi(const float &dInput)
{
std::cout << "dInput = " << dInput << std::endl;
std::cout << "dInput 地址 " << &dInput << std::endl;
}
long lNum = 1L;
float fNum = 1L;
aiZuoNi(lNum);
std::cout << "lNum = " << lNum << std::endl;
std::cout << "lNum 地址 " << &lNum << std::endl;
aiZuoNi(fNum);
std::cout << "fNum = " << fNum << std::endl;
std::cout << "fNum 地址 " << &fNum << std::endl;
aiZuoNi(2.0);
aiZuoNi(fNum + 1);
上述代碼的運行結果是:
可以看到地址一樣的只有aiZuoNi(fNum)調用的情況。
《C++ Primer Plus》給出的三個使用常量引用的理由是:
1、可以避免無意中修改數據的變成錯誤;
2、能夠處理const和非const實參,否則只能使用非const數據,參見aiZuoNiJiuZuoNi(dNum1 + 1); // ERROR
3、使函數能夠正確生成並使用臨時變量
3、引用和指針的區別
-
聲明方式不同,引用聲明的時候必須初始化定義,指針可以先聲明,再定義指向某個地址;
-
指針是需要分配內存的,指針本身占用內存來存儲變量地址,引用不用,引用是原變量的別名,就還是原變量自己;
-
有多級指針,沒見過多級引用;
-
sizeof(引用)得到的是所指向的變量(對象)的大小,而sizeof(指針)得到的是指針本身的大小,而不是指向對象的大小;
-
指針自增減是地址的增減。引用的自增減是變量值得自增減;
-
函數引用參數是類型安全,指針參數則不是(故常見指針參數需判斷指針是否有效);
-
引用是從一而終,指針是見異思遷;
假如你不夠快樂
也不要把眉頭深鎖
人生本來短暫
為什么 還要栽培苦澀
打開塵封的門窗
讓陽光雨露灑遍每個角落
走向生命的原野
讓風兒熨平前額
博大可以稀釋憂愁
深色能夠覆蓋淺色