C++引用的使用與const修飾符
1、引用
引用是給已經(jīng)定義的變量一個(gè)別名,可以簡(jiǎn)單理解成同一個(gè)變量的昵稱。既然是昵稱或者是別名,顯然它和原本的變量名有著同樣的效力。所以我們對(duì)別名進(jìn)行修改,原本的變量值也一樣會(huì)發(fā)生變化。
我們通過符號(hào)&
來表明引用,
比如下面這個(gè)例子,我們創(chuàng)建了a變量的一個(gè)引用b
int a = 3; int &b = a; b++; cout << a << endl;
由于b是a的一個(gè)引用,本質(zhì)上來說它們是同一個(gè)變量,只不過名稱不同。所以我們對(duì)b修改,等價(jià)于對(duì)a進(jìn)行同樣的修改。所以輸出的結(jié)果是4。
也就是說我們需要把引用變量和原變量當(dāng)成是同樣的變量,只不過名稱不同,其中一個(gè)發(fā)生變化,另外一個(gè)一樣會(huì)生效。
看上去有些像是指針,因?yàn)閯?chuàng)建指針也能有類似的效果:
int a = 3; int *p = &a; *p++; cout << a << endl;
但是引用和指針還是有些區(qū)別,這個(gè)問題在C++
相關(guān)的面試當(dāng)中經(jīng)常會(huì)問到,也是作為基本功的考察之一。
首先一個(gè)區(qū)別是,引用必須在聲明的時(shí)候就進(jìn)行初始化,沒辦法先聲明再賦值:
int *pt; // 合法 int &b; // 非法
從這個(gè)角度來說,引用更接近c(diǎn)onst指針,一旦與某個(gè)變量關(guān)聯(lián)就不能再指向其他變量:
int &b = a; // 等價(jià)于 int *const pt = &a;
在這個(gè)例子當(dāng)中,b等價(jià)于*pt。
如果我們輸出引用和原變量的地址,會(huì)得到同樣的結(jié)果:
int a = 3; int &b = a; cout << &a << " " << &b << endl;
2、函數(shù)引用傳遞
其實(shí)到這里有一個(gè)問題,既然引用只是別名,我們已經(jīng)有了原本的變量名可以用了,又何必多此一舉創(chuàng)建變量的引用呢?
所以引用不是為了順序執(zhí)行的邏輯創(chuàng)建的,一個(gè)最常見的使用場(chǎng)景就是函數(shù)參數(shù)傳遞的時(shí)候,可以設(shè)置函數(shù)接收的變量類型為引用。
如:
void swap1(int& a, int& b) { int temp = b; b = a; a = temp; } void swap2(int a, int b) { int temp = b; b = a; a = temp; }
我們創(chuàng)建了兩個(gè)swap
函數(shù),其中一個(gè)傳遞的參數(shù)是引用,另外一個(gè)就是普通的值傳遞。如果大家去分別調(diào)用這兩個(gè)函數(shù)進(jìn)行嘗試,會(huì)發(fā)現(xiàn)swap2
函數(shù)沒有生效。
因?yàn)橹祩鬟f的時(shí)候,會(huì)發(fā)生拷貝,也就是說函數(shù)內(nèi)部接受的其實(shí)是變量的拷貝。我們對(duì)于拷貝無論如何修改也不會(huì)影響原值,而傳引用就不一樣了。前面說過,引用和原變量是等價(jià)的。我們對(duì)引用進(jìn)行修改等價(jià)于對(duì)原變量進(jìn)行修改。
這樣的話,我們就可以實(shí)現(xiàn)在函數(shù)體內(nèi)部對(duì)外部傳入的參數(shù)進(jìn)行修改。在一些特殊的場(chǎng)景當(dāng)中,非常方便。比如一些復(fù)雜的樹形數(shù)據(jù)結(jié)構(gòu),通過使用引用可以大大降低代碼的編寫難度。
除此之外,使用引用還有一個(gè)好處,既然我們傳遞的引用和原值是等價(jià)的。那么也就免去了拷貝變量的開銷,如果我們傳遞的是int,double
這樣的變量還好,如果是一個(gè)包含大量元素的容器,如vector
,set
,map
等,使用引用傳遞可以帶來明顯的效率提升,也會(huì)降低內(nèi)存開銷。
3、引用與const
前文當(dāng)中說過,我們可以讓函數(shù)接收一個(gè)引用變量,從而免去變量拷貝的開銷,達(dá)到提升程序運(yùn)行效率的目的。
如果我們想要傳遞引用,但又不希望在函數(shù)內(nèi)部對(duì)引用的變量進(jìn)行修改,以免影響外部變量。我們可以使用常量引用,也就是加上const
修飾符。
double sqrt(const double &x);
由于我們加上了const
修飾符,當(dāng)我們?cè)诤瘮?shù)內(nèi)部對(duì)引用進(jìn)行修改的時(shí)候,會(huì)觸發(fā)編譯器的報(bào)錯(cuò)。一般來說,如果傳遞的只是基本類型的變量,我們其實(shí)沒有必要這么操作,直接值傳遞即可。這種做法一般用在傳遞一些大型結(jié)構(gòu)體或者是大型容器的時(shí)候。
這里有一個(gè)小細(xì)節(jié)需要當(dāng)心,由于我們傳遞的是引用,需要保證傳遞的參數(shù)是一個(gè)實(shí)參,而不是表達(dá)式。如這樣的代碼編譯時(shí)會(huì)報(bào)錯(cuò):
double distance(double &x, double &y) { return sqrt(x * x + y * y); } int main() { double x = 3.0, y = 4.0; cout << distance(x + 3.0, y + 4.0); }
報(bào)錯(cuò)的原因在于,函數(shù)distance
接收的是一個(gè)double
類型的引用,而我們傳遞的卻是x+3
這樣的表達(dá)式。顯然表達(dá)式?jīng)]有對(duì)應(yīng)的引用。所以編譯器會(huì)報(bào)錯(cuò),告訴我們參數(shù)類型不匹配:
但神奇的是,如果我們把函數(shù)簽名稍微改一下,加上const修飾符,會(huì)發(fā)現(xiàn)報(bào)錯(cuò)消失了:
double distance(const double &x, const double &y) { return sqrt(x * x + y * y); }
這并不是編譯器的bug
,而是編譯器針對(duì)const
引用做了特殊處理。當(dāng)編譯器發(fā)現(xiàn)傳入的不是double類型的變量的時(shí)候,它會(huì)創(chuàng)建一個(gè)臨時(shí)的無名變量,將這個(gè)臨時(shí)變量初始化成x+3.0
,然后再傳入這個(gè)臨時(shí)變量的引用。C++只會(huì)對(duì)const引用參數(shù)執(zhí)行這個(gè)操作。
除了表達(dá)式之外,如果變量的類型不匹配也一樣會(huì)創(chuàng)建臨時(shí)變量。這些臨時(shí)變量只會(huì)在函數(shù)調(diào)用期間存在,函數(shù)運(yùn)行結(jié)束之后,編譯器會(huì)將其刪除。
為什么會(huì)有這樣的設(shè)計(jì)呢?C++ Primer當(dāng)中提供了這樣一個(gè)例子:
void swapr(int &a, int &b) { int temp = b; b = a; a = temp; } long a = 3, b = 5; swapr(a, b);
在早期C++沒有嚴(yán)格限制的情況下,這段代碼會(huì)發(fā)生什么呢?
由于類型不匹配,所以編譯器會(huì)創(chuàng)建兩個(gè)臨時(shí)的int變量,但它們初始化成3和5,再傳入函數(shù)當(dāng)中。然后執(zhí)行函數(shù)當(dāng)中交換變量的邏輯,但問題是,我們交換的是兩個(gè)臨時(shí)變量,原變量并不會(huì)生效。
所以后來版本的C++優(yōu)化了這個(gè)問題,禁止了傳遞引用時(shí)創(chuàng)建臨時(shí)變量。而當(dāng)引用有const修飾時(shí)并不會(huì)對(duì)原值進(jìn)行修改,并不會(huì)影響邏輯和結(jié)果,所以豁免了這個(gè)禁令。
4、const修飾符的優(yōu)點(diǎn)
在函數(shù)簽名當(dāng)中,如果要接收引用,我們要盡可能使用const,我們來看下這樣做的好處:
- 可以避免無意中修改數(shù)據(jù)
- 可以處理
const
和非const
參數(shù),否則,只能接受非const
變量 - 可以接受臨時(shí)變量
到此這篇關(guān)于C++引用的使用與const修飾符的文章就介紹到這了,更多相關(guān)C++引用與const內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
這篇文章轉(zhuǎn)自公眾號(hào):Coder梁(ID:Coder_LT)
相關(guān)文章
C語言 ffmpeg與sdl實(shí)現(xiàn)播放視頻同時(shí)同步時(shí)鐘詳解
使用ffmpeg和sdl實(shí)現(xiàn)播放視頻后,需要再實(shí)現(xiàn)時(shí)鐘同步才能正常的播放視頻,尤其是有音頻的情況,我們通常需要將視頻同步到音頻來確保音畫同步2022-09-09OpenCV實(shí)現(xiàn)鼠標(biāo)框選并顯示框選區(qū)域
這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)鼠標(biāo)框選并顯示框選區(qū)域,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08C++ WideCharToMultiByte()函數(shù)案例詳解
這篇文章主要介紹了C++ WideCharToMultiByte()函數(shù)案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08C++實(shí)現(xiàn)LeetCode(692.前K個(gè)高頻詞)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(692.前K個(gè)高頻詞),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08三種獲取網(wǎng)頁源碼的方法(使用MFC/Socket實(shí)現(xiàn))
Windows下比較簡(jiǎn)單的獲取網(wǎng)頁源碼的方法:使用MFC、使用MFC、Socket實(shí)現(xiàn)2013-12-12詳解如何利用C++實(shí)現(xiàn)Mystring類
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)MyString的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08