C++深入探究引用的使用_第1頁
C++深入探究引用的使用_第2頁
C++深入探究引用的使用_第3頁
C++深入探究引用的使用_第4頁
C++深入探究引用的使用_第5頁
已閱讀5頁,還剩7頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第C++深入探究引用的使用目錄一.引用的概念二.引用特性三.常引用四.使用場景1.做參數(shù)2.做返回值3.做返回值需要注意的問題五.傳值傳引用效率對比1.值和引用傳參時的效率比較2.值和引用的作為返回值類型的性能比較六.引用和指針

一.引用的概念

引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間。

類型引用變量名(對象名)=引用實(shí)體;

如下:

voidTestRef()

inta=10;

intra=a;//====定義引用類型

printf("%p\n",

printf("%p\n",ra);

}

注意:引用類型必須和引用實(shí)體是同種類型的

二.引用特性

1.引用在定義時必須初始化

2.一個變量可以有多個引用

3.引用一旦引用一個實(shí)體,再不能引用其他實(shí)體

如下:

voidTestRef()

inta=10;

inta2=20;

//a的多個引用

intb=a;

intc=a;

intd=b;

intra;//該條語句編譯時會出錯,未初始化

intra=a2;//報錯,引用了其他實(shí)體

printf("%p%p%p%p\n",a,b,c,

}

三.常引用

voidTestConstRef()

constinta=10;

//intra=a;//該語句編譯時會出錯,a為常量

constintra=a;

//intb=10;//該語句編譯時會出錯,b為常量

constintb=10;

doubled=12.34;

//intrd=d;//該語句編譯時會出錯,類型不同

constintrd=d;

//intc=100;//該語句編譯時會出錯,常量是只讀的

constintc=100;

}

注意:

引用取別名原則:對原引用變量,讀寫權(quán)限只能縮小,不能放大

constinta=10;

intra=a;

編譯不通過,因為放大了權(quán)限,原引用本來是只讀,但是引用以后卻變成了可讀可寫

intb=10;

constintb=10;

編譯可以通過,因為縮小了權(quán)限,原引用本來是可讀可寫,引用后變成了只讀

doubled=12.34;

intrd=d;

編譯不通過,這里比較特殊,看起來是因為類型不同而報錯,其實(shí)不然,報錯是因為權(quán)限放大了,為什么?

int類型要引用double類型,double類型轉(zhuǎn)化到int類型屬于隱式類型轉(zhuǎn)換會舍棄小數(shù)位,隱式類型轉(zhuǎn)換會產(chǎn)生臨時變量,double類型到int類型會創(chuàng)建一個臨時變量存儲double變成了int類型的值,這里需要注意,這個臨時變量具有常性是只讀的,rd其實(shí)是引用了這個臨時變量,因為臨時變量是只讀的,引用了臨時變量的rd也應(yīng)該是只讀的,所以這就是為什么constintrd=d可以編譯通過。

intc=100;

編譯通過,因為常量本來就是只讀的,不加const代表引用后變成了可讀可寫,權(quán)限放大。

四.使用場景

1.做參數(shù)

voidSwap(intleft,intright)

inttemp=left;

left=right;

right=temp;

}

輸出型參數(shù)減少拷貝,提高效率

2.做返回值

intCount()

staticintn=0;

n++;

//...

returnn;

}

減少拷貝

(傳值返回需要拷貝數(shù)據(jù),傳引用返回直接返回變量的別名)

3.做返回值需要注意的問題

首先,我們要知道當(dāng)函數(shù)返回一個值時,會生成一個臨時變量,而函數(shù)的返回類型就是這個臨時變量的類型

intAdd(inta,intb)

returna+b;

intCount()

staticintn=0;

n++;

returnn;

intmain()

inttemp=Add(2,3);

inttmp=Count();

return0;

}

以上代碼將a+b(n)的值賦值給臨時變量,臨時變量再賦值給temp(tmp),為什么要設(shè)置這個臨時變量?

其實(shí)很簡單,在這個代碼里是會有問題的,出了函數(shù)作用域a+b的值就已經(jīng)被銷毀了,需要一個臨時變量去儲存這個返回值,再去訪問那塊空間是非法的,而被static修飾的n由于它的生命周期變長了,即使出了函數(shù)也不會被銷毀

那么問題來了,以下代碼是正確的嗎?

intAdd(inta,intb)

intc=a+b;

returnc;

intmain()

intret=Add(1,2);

return0;

}

很明顯是有問題的!這里將c的引用返回,而一旦出了函數(shù)c就被銷毀了,這塊空間也被操作系統(tǒng)收回,再將c的引用賦值給ret就變成了非法訪問了,就變成了由引用造成的野指針

由上面的問題可以衍生出以下代碼:

這里的ret是什么?

intAdd(inta,intb)

intc=a+b;

returnc;

intmain()

intret=Add(1,2);

Add(3,4);

cout"Add(1,2)is:"retendl;

return0;

}

很明顯是7,ret是c的引用,由于出了函數(shù)以后這塊空間的使用權(quán)還給了操作系統(tǒng),由于第二次函數(shù)調(diào)用仍然是在第一次函數(shù)調(diào)用的空間進(jìn)行棧幀的建立,因為ret的地址(ret的地址就是之前那塊臨時變量的地址)還是之前那個地址,所以由于第二次返回c時建立的臨時變量已經(jīng)變成了7,所以ret也變成了7

但是一定會是7嗎?其實(shí)不然,我們知道這塊空間的使用權(quán)還給了操作系統(tǒng),這塊空間也有可能會被其他程序使用了,導(dǎo)致數(shù)值變成了不確定性,因為這里是直接馬上又調(diào)用了這個函數(shù),所以會是7,所以,其實(shí)正確答案應(yīng)該是隨機(jī)值才對

看下面這個代碼就是典型的例子:

intAdd(inta,intb)

intc=a+b;

returnc;

intmain()

intret=Add(1,2);

Add(3,4);

cout"Add(1,2)is:"retendl;

cout"Add(1,2)is:"retendl;

return0;

}

這里的輸出語句其實(shí)也是調(diào)用了函數(shù),由上面可知第一個是7,那么第二個呢?隨機(jī)值!因為進(jìn)行了第一次輸出后其實(shí)也是進(jìn)行了函數(shù)調(diào)用,函數(shù)調(diào)用會建立棧幀,在上一個輸出建立的棧幀處重新建立了棧幀,函數(shù)調(diào)用前需要先傳參,由于上一個輸出語句銷毀完棧幀以后ret地址處的值被覆蓋成隨機(jī)值,在第二次輸出語句中此時就會把這個隨機(jī)值作為參數(shù)傳過去給函數(shù),導(dǎo)致輸出了隨機(jī)值,所以傳引用返回不是所有情況都可以使用的,像一開始加上了static關(guān)鍵字之類的才可以返回,因為n的生命周期變長了,出了函數(shù)作用域沒有被銷毀,取值都是去靜態(tài)區(qū)取數(shù)據(jù)。

結(jié)論:如果函數(shù)返回時,出了函數(shù)作用域,如果返回對象還未還給系統(tǒng),則可以使用引用返回,如果已經(jīng)還給系統(tǒng)了,則必須使用傳值返回。

五.傳值傳引用效率對比

以值作為參數(shù)或者返回值類型,在傳參和返回期間,函數(shù)不會直接傳遞實(shí)參或者將變量本身直接返回,而是傳遞實(shí)參或者返回變量的一份臨時的拷貝,因此用值作為參數(shù)或者返回值類型,效率是非常低下的,尤其是當(dāng)參數(shù)或者返回值類型非常大時,效率就更低。

1.值和引用傳參時的效率比較

#includetime.h

structA{

inta[10000];

voidTestFunc1(Aa){}

voidTestFunc2(Aa){}

voidTestFunc3(A*a){}

voidTestRefAndValue()

Aa;

//以值作為函數(shù)參數(shù)

size_tbegin1=clock();

for(size_ti=0;i10000;++i)

TestFunc1(a);

size_tend1=clock();

//以引用作為函數(shù)參數(shù)

size_tbegin2=clock();

for(size_ti=0;i10000;++i)

TestFunc2(a);

size_tend2=clock();

//以指針作為參數(shù)

size_tbegin3=clock();

for(size_ti=0;i10000;++i)

TestFunc3(

size_tend3=clock();

//分別計算兩個函數(shù)運(yùn)行結(jié)束后的時間

cout"TestFunc1(A)-time:"end1-begin1endl;

cout"TestFunc2(A)-time:"end2-begin2endl;

cout"TestFunc2(A)-time:"end3-begin3endl;

}

2.值和引用的作為返回值類型的性能比較

#includetime.h

structA{inta[10000];};

//值返回

ATestFunc1(){returna;}

//引用返回

ATestFunc2(){returna;}

voidTestReturnByRefOrValue()

//以值作為函數(shù)的返回值類型

size_tbegin1=clock();

for(size_ti=0;i100000;++i)

TestFunc1();

size_tend1=clock();

//以引用作為函數(shù)的返回值類型

size_tbegin2=clock();

for(size_ti=0;i100000;++i)

TestFunc2();

size_tend2=clock();

//計算兩個函數(shù)運(yùn)算完成之后的時間

cout"TestFunc1time:"end1-begin1endl;

cout"TestFunc2time:"end2-begin2endl;

}

通過上述代碼的比較,發(fā)現(xiàn)傳值和指針在作為傳參以及返回值類型上效率相差很大。

六.引用和指針

在語法概念上引用就是一個別名,沒有獨(dú)立空間,和其引用實(shí)體共用同一塊空間。

intmain()

inta=10;

intra=a;

cout"a="aendl;

cout"ra="raendl;

return0;

}

在底層實(shí)現(xiàn)上實(shí)際是有空間的,因為引用是按照指針方式來實(shí)現(xiàn)的。

intmain()

inta=10;

intra=a;

ra=20;

int*pa=

*pa=20;

return0;

}

我們來看下引用和指針的匯編代碼對比:

引用和指針的不同點(diǎn):

引用在定義時必須初始化,指針沒有要求(建議初始化)引用在初始化時引用一個實(shí)體后,就不能再引用其他實(shí)體,而指針可以在任何時候指向任何一個同類型實(shí)體沒有NULL引用,但有NULL指針在sizeof中含義不同:引用結(jié)果為引用類型的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論