C++ 基础 : 函数重载、引用、内联函数、auto、范围for循环
- 函數(shù)重載
- 引用
- 內(nèi)聯(lián)函數(shù)
- auto
- 范圍for循環(huán)
函數(shù)重載
C++中引入了一個(gè)新特性,函數(shù)重載。
在同一個(gè)作用域下,對(duì)于相同的函數(shù)名,函數(shù)的參數(shù)不同,不同類型的參數(shù)順序不同,參數(shù)的個(gè)數(shù)不同,都可以形成函數(shù)的重載(參數(shù)名不同,返回值不同不形成重載)
函數(shù)的重載主要用于處理功能相同,類型不同的數(shù)據(jù)。
例如
int test(int i, int j) {cout << "test" << endl; }int test(double i, int j) {cout << "test" << endl; }int test(double i, int j, int k) {cout << "test" << endl; }為什么C++支持重載,C語言不支持呢?
因?yàn)閣indows的處理更加復(fù)雜,所以這里用linux下的gcc和g++來看更加直觀。
首先我們要知道,鏈接器看到有函數(shù)被調(diào)用的時(shí)候,就會(huì)到符號(hào)表中去查找對(duì)應(yīng)的函數(shù)名,來獲取函數(shù)的地址,再鏈接到一起
先看C語言是怎么處理的
通過反匯編我們可以看到,C語言并沒有對(duì)函數(shù)名進(jìn)行處理,也就是說無論我們參數(shù)的個(gè)數(shù),參數(shù)的類型,參數(shù)的順序怎么修改,它只認(rèn)函數(shù)名,如果出現(xiàn)了第二個(gè)相同函數(shù)名的,就算重定義。
下面再看C++的
這里可以看到,C++對(duì)函數(shù)名進(jìn)行了處理,函數(shù)以_Z4開頭,接著是函數(shù)名,最后是所有參數(shù)的縮寫。
_Z是所有函數(shù)的前綴,4是函數(shù)名的字符個(gè)數(shù),例如第一個(gè)_Z4testii則代表函數(shù)名為test,具有四個(gè)字符,參數(shù)分別是ii。
這也就是為什么返回值不同和參數(shù)名不構(gòu)成重載的原因,C++正是通過這種函數(shù)名修飾規(guī)則來實(shí)現(xiàn)函數(shù)的重載。
extern “C”
有時(shí)候我們?cè)谑褂肅++的使用,對(duì)于某些函數(shù)想讓他按照C的風(fēng)格來編譯,就在函數(shù)前加extern “C”,意思是告訴編譯器,將該函數(shù)按照C語言規(guī)則來編譯。
引用
引用的概念
引用是給某一個(gè)對(duì)象起了另外一個(gè)名字,可以通過這個(gè)別名來對(duì)原對(duì)象進(jìn)行操作,同時(shí)編譯器不會(huì)對(duì)引用變量開辟空間,它和它所引用的對(duì)象共用一個(gè)空間。
用法:類型 & 引用對(duì)象名 = 引用實(shí)體
int main() {int i = 5;int& j = i;cout << i << ' ' << j << endl;j = 8;cout << i << ' ' << j << endl;return 0; }引用的特性
常引用
int main()
{const int i = 5;int& j = i;//錯(cuò)誤的const int & k = i;正確的 }這里對(duì)常量i進(jìn)行引用,因?yàn)閕是個(gè)常量,所以它的值是無法修改的,所以我們使用普通的引用的時(shí)候,就會(huì)將它的權(quán)限放大,導(dǎo)致可以通過j來對(duì)i進(jìn)行修改,這是不合理的,所以編譯器會(huì)報(bào)錯(cuò)。只有使用常量引用才行
int main() {int x = 6;const int & y = x; }同時(shí),我們用常量引用來引用這個(gè)x,x是可以修改的,而y無法修改,使權(quán)限收縮,變?yōu)橹蛔x,所以這是可行的。
引用的跨類型
int main() {double x = 3.14;int& y = x;//錯(cuò)誤的const int& z = x;//正確的return 0; }這里我們分別使用y和z來引用x,但是這時(shí)編譯器會(huì)提示,y報(bào)錯(cuò),z合法,同樣是跨類型,為什么會(huì)這樣呢?
因?yàn)楫?dāng)類型不同的時(shí)候會(huì)先用一個(gè)臨時(shí)量來引用x,然后再對(duì)這個(gè)臨時(shí)量進(jìn)行引用
int main() {double x = 3.14;int& y = x;/*等價(jià)于const int &temp = x;int &y = temp;*/const int& y = x;/*等價(jià)于const int &temp = x;const int &z = temp;*/return 0; }如果是普通的引用,這時(shí)我們引用的實(shí)際上是temp,但是我們是想要對(duì)x進(jìn)行修改的,但是這時(shí)不行的,因?yàn)榕R時(shí)量具有常性,所以這種行為也是非法的。
如果是常量的引用,表明我們不會(huì)對(duì)x進(jìn)行修改,所以就算引用的其實(shí)是臨時(shí)量temp,這個(gè)行為也是合法的
引用的使用場(chǎng)景
假設(shè)我們存在一個(gè)超級(jí)大的結(jié)構(gòu)體,如果我們直接將結(jié)構(gòu)體傳過去的話,會(huì)產(chǎn)生一個(gè)臨時(shí)變量來將這個(gè)結(jié)構(gòu)體拷貝到形參中,這是極大的開銷,但如果我們使用引用的話,傳的只是一個(gè)別名而已,但是需要注意的和上面一樣,因?yàn)榕R時(shí)量具有常性,所以如果我們要傳遞一個(gè)常量,就必須要在引用前加上const。
對(duì)于這樣一個(gè)代碼,我們可能第一眼覺得ret會(huì)是3
但是其實(shí)是7.
因?yàn)槲覀兎祷氐氖莄的一個(gè)引用,但是c是只存在于調(diào)用時(shí)的那個(gè)棧幀的,調(diào)用結(jié)束后那個(gè)棧幀就會(huì)被銷毀,雖然銷毀后數(shù)據(jù)不會(huì)被清空,但是那片區(qū)域的訪問權(quán)限就會(huì)被放開,有可能會(huì)被下次調(diào)用的函數(shù)使用,也有可能會(huì)被哪里的一個(gè)操作給使用,所以這是一種極為不安全的行為,如果我們要返回一個(gè)引用的話,就必須要保證那個(gè)對(duì)象出了函數(shù)的作用域中還會(huì)仍然存在。
上面的7是第二次調(diào)用后修改了c的值。
所以,如果需要引用作為返回值,就必須保證出了函數(shù)作用域,返回的對(duì)象沒有歸還給系統(tǒng),仍然存在。
以值作為參數(shù)或者返回值時(shí),在傳參和返回的時(shí)候,都會(huì)傳遞或返回原變量的一個(gè)臨時(shí)的拷貝,這樣的效率是非常低下的,尤其是數(shù)據(jù)特別大的時(shí)候,但如果使用引用作為參數(shù)的話,就不會(huì)有這樣的問題。
引用和指針
前面我們說了,引用是對(duì)象的一個(gè)別名,沒有獨(dú)立的空間,和其引用的實(shí)體共用一個(gè)空間,但是這僅僅是語法概念上的。同時(shí)我們發(fā)現(xiàn),引用其實(shí)和指針很像,它更像一個(gè)頂層const的指針,所以我們可以進(jìn)入反匯編看看他們之間有沒有關(guān)系
int main() {int x = 5;int& y = x;int* z = &x;return 0; }
反匯編下我們可以看到,指針和引用在匯編下的實(shí)現(xiàn)竟然是一模一樣的。
所以我們可以得出一個(gè)結(jié)論,引用是按照指針來實(shí)現(xiàn)的,在指針的基礎(chǔ)上又給他封裝了新的功能
引用和指針的不同點(diǎn):
內(nèi)聯(lián)函數(shù)
用inline關(guān)鍵字修飾的函數(shù)就是內(nèi)聯(lián)函數(shù),在編譯時(shí)編譯器會(huì)將函數(shù)的代碼在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開,減去了函數(shù)壓棧的開銷,提升程序運(yùn)行的效率
例如這樣一個(gè)簡(jiǎn)單的代碼
如果我們直接調(diào)用它
在匯編下可以看到,他會(huì)創(chuàng)建一個(gè)新的棧幀,將參數(shù)3,4壓棧,然后計(jì)算完再返回結(jié)果
而如果在函數(shù)前面加上inline使其變?yōu)閮?nèi)聯(lián)函數(shù)
這時(shí)再看,就會(huì)發(fā)現(xiàn)它直接把函數(shù)的代碼在調(diào)用處直接展開,不會(huì)再創(chuàng)建新的棧幀。
內(nèi)聯(lián)函數(shù)的特性
- 內(nèi)聯(lián)函數(shù)是一種用空間換時(shí)間的做法,省去了創(chuàng)建棧幀和壓棧的開銷,但也因此代碼很復(fù)雜和具有循環(huán)或遞歸之類的函數(shù)不適合作為內(nèi)聯(lián)函數(shù),就算聲明為內(nèi)聯(lián)函數(shù)編譯器也會(huì)自動(dòng)將其忽略。
- 內(nèi)聯(lián)函數(shù)不能聲明和定義分離,因?yàn)橐坏┞暶鳛閮?nèi)聯(lián)函數(shù),在調(diào)用的時(shí)候就會(huì)直接展開,沒有了函數(shù)的地址,就無法將其鏈接到定義的部分。
值得一提的是,內(nèi)聯(lián)函數(shù)與C語言中的宏函數(shù)有些類似,雖然宏的性能不錯(cuò),但是因?yàn)楹耆狈︻愋偷陌踩珯z查和無法調(diào)試(在預(yù)處理階段就進(jìn)行了宏替換),在C++中宏函數(shù)被內(nèi)聯(lián)函數(shù)替代,宏常量定義被const取代。
auto
在編程時(shí)我們常常需要把表達(dá)式的值賦給變量,但有時(shí)我們又不知道表達(dá)式的類型是什么,為了解決這個(gè)問題,C++11中引入了auto類型說明符,用它能夠讓編譯器代替我們來分析表達(dá)式的所屬類型。
因?yàn)閍uto需要讓編譯器通過初始值來推導(dǎo)變量的類型,所以auto必須要有初始值。
int main() {auto i = 2.7;cout << i <<"的類型為:" << typeid(i).name() << endl; }auto的使用規(guī)則
因?yàn)閍uto可以識(shí)別指針類型,所以加不加*都可以,但是引用必須加上&,因?yàn)镃++沒有引用這個(gè)類型,引用只是一個(gè)修飾別名,所以它的本質(zhì)還是int。
auto不能作為函數(shù)的參數(shù)
auto不能作為形參的類型,編譯器無法對(duì)i和j的類型進(jìn)行推導(dǎo)。
auto不能直接用來聲明數(shù)組
auto一般會(huì)忽略頂層const
范圍for循環(huán)
對(duì)于一些有范圍的集合,我們可能不知道他的長度或者在使用循環(huán)的時(shí)候不小心越界,c++11為了解決這個(gè)問題,引入了范圍for循環(huán)。
循環(huán)分為兩部分,第一部分是變量的類型,第二部分是被迭代的范圍,兩者中間用:隔開.
int main() {vector<int> vec{ 1, 3, 5, 7, 9 };for (auto i : vec){cout << i << ends;}}
需要注意的是,這里的i是對(duì)原數(shù)據(jù)拷貝的一個(gè)臨時(shí)變量,如果要修改原數(shù)據(jù)的話需要改成&i(既對(duì)原數(shù)據(jù)的引用)
總結(jié)
以上是生活随笔為你收集整理的C++ 基础 : 函数重载、引用、内联函数、auto、范围for循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 基础I/O :文件描述符,重
- 下一篇: C++ 类和对象(一):类的概念、类的访