6.1 函數(shù)基礎(chǔ)
一個(gè)典型的函數(shù)定義包括如下部分:返回類型、函數(shù)名、參數(shù)列表以及函數(shù)體盛杰。
通過(guò)調(diào)用運(yùn)算符(call operator)來(lái)執(zhí)行函數(shù)。調(diào)用運(yùn)算符的形式是一對(duì)圓括號(hào)藐石,它作用于一個(gè)表達(dá)式即供,該表達(dá)式是函數(shù)或者指向函數(shù)的指針。
函數(shù)的調(diào)用完成兩項(xiàng)工作:1.用實(shí)參初始化函數(shù)對(duì)應(yīng)的形參 2.將控制權(quán)轉(zhuǎn)移給被調(diào)用函數(shù)于微,此時(shí)主調(diào)函數(shù)的執(zhí)行被暫時(shí)中斷逗嫡,被調(diào)函數(shù)開始執(zhí)行。
當(dāng)遇到一條return語(yǔ)句時(shí)函數(shù)結(jié)束執(zhí)行的過(guò)程角雷。和函數(shù)調(diào)用一樣祸穷,return語(yǔ)句也完成兩項(xiàng)工作:1.返回return語(yǔ)句中的值(如果有的話)2.將控制權(quán)從被調(diào)函數(shù)轉(zhuǎn)移回主調(diào)函數(shù)。
函數(shù)的返回類型不能是數(shù)組類型或者函數(shù)類型勺三,但可以是指向數(shù)組或者函數(shù)的指針雷滚。
在所有函數(shù)體之外定義的對(duì)象存在于程序的整個(gè)執(zhí)行過(guò)程中。此類對(duì)象在程序啟動(dòng)時(shí)被創(chuàng)建吗坚,直到程序結(jié)束才銷毀祈远。
自動(dòng)對(duì)象:對(duì)于普通局部變量對(duì)應(yīng)的對(duì)象來(lái)說(shuō)呆万,當(dāng)函數(shù)的控制路徑經(jīng)過(guò)變量定義語(yǔ)句時(shí)創(chuàng)建該對(duì)象,當(dāng)?shù)竭_(dá)定義所在的塊末尾時(shí)銷毀它车份。把只存在于塊執(zhí)行期間的對(duì)象稱為自動(dòng)對(duì)象(automatic object)谋减。當(dāng)塊的執(zhí)行結(jié)束后,塊中創(chuàng)建的自動(dòng)對(duì)象的值就變成未定義的了扫沼。
局部靜態(tài)對(duì)象:某些時(shí)候出爹,有必要令局部變量的生命周期貫穿函數(shù)調(diào)用及之后的時(shí)間,此時(shí)可以將局部變量定義成static類型缎除。
類似于變量严就,函數(shù)只能定義一次,但可以聲明多次器罐。函數(shù)聲明無(wú)須函數(shù)體梢为,用一個(gè)分號(hào)替代即可。因?yàn)楹瘮?shù)的聲明不包含函數(shù)體轰坊,所以也就無(wú)須形參的名字铸董。事實(shí)上,在函數(shù)的聲明中經(jīng)常省略形參的名字肴沫。盡管如此粟害,寫上形參的名字還是有用處的,可以幫助使用者更好理解函數(shù)的功能樊零。函數(shù)聲明也被稱作函數(shù)原型(function prototype)我磁。
之前曾建議過(guò)變量在頭文件中聲明,在源文件中定義驻襟。函數(shù)也應(yīng)如此夺艰。
分離式編譯:允許我們把程序分割到幾個(gè)文件中去,每個(gè)文件獨(dú)立編譯沉衣。
6.2 參數(shù)傳遞
如果形參是引用類型郁副,它將綁定到對(duì)應(yīng)的實(shí)參上;否則豌习,將實(shí)參的值拷貝后賦給形參存谎。
當(dāng)形參是引用類型時(shí),我們說(shuō)它對(duì)應(yīng)的實(shí)參被引用傳遞肥隆。如果是拷貝既荚,則說(shuō)這樣的實(shí)參被值傳遞。
使用引用避免拷貝:拷貝大的類類型對(duì)象或者容器對(duì)象比較低效栋艳,甚至有的類型根本就不支持拷貝操作恰聘。如比較兩個(gè)string對(duì)象長(zhǎng)度的函數(shù),形參就可以為const string &s1...這既避免了引用操作,又能保證string對(duì)象不被修改晴叨。
使用引用參數(shù)可以返回額外的信息凿宾,如一個(gè)函數(shù)想返回多個(gè)參數(shù),那么可以選擇return一種新的數(shù)據(jù)類型兼蕊,讓它包含多個(gè)信息成員初厚。另一種更簡(jiǎn)單的辦法就是給函數(shù)傳入一個(gè)額外的引用實(shí)參,讓其來(lái)保存信息孙技。
數(shù)組的兩個(gè)特殊性質(zhì):不允許拷貝數(shù)組产禾,使用數(shù)組時(shí)(通常)會(huì)將其轉(zhuǎn)換成指針。因此绪杏,我們無(wú)法以值傳遞方式使用數(shù)組參數(shù)下愈,所以當(dāng)為函數(shù)傳遞一個(gè)數(shù)組時(shí)纽绍,實(shí)際上傳遞的時(shí)指向數(shù)組首元素的指針蕾久。
void print(const int*);? ?
void print(const int[]);?
void print(const int[10]);//這里的維度表示我們期望數(shù)組含有多少元素,實(shí)際不一定
盡管形式不同拌夏,這三個(gè)函數(shù)是等價(jià)的僧著。每個(gè)函數(shù)的唯一形參都是const int*類型的。
int (*matrix)[10]; //指向含有10個(gè)整數(shù)的數(shù)組的指針
void print(int matrix[][10], int rowSize) { /*...*/ }? ?//等價(jià)定義
下面print函數(shù)中matrix的聲明看起來(lái)是一個(gè)二維數(shù)組障簿,實(shí)際上形參是指向含有10個(gè)整數(shù)的數(shù)組的指針盹愚。因?yàn)榫幾g器會(huì)一如既往地忽略掉第一個(gè)維度,所以最好不要把它包括在形參列表內(nèi)站故。
為了編寫能處理不同數(shù)量實(shí)參的函數(shù)皆怕。c++11提供了兩種主要方法:1.如果所有實(shí)參類型相同,可以傳遞名為initializer_list的標(biāo)準(zhǔn)庫(kù)類型西篓;如果實(shí)參的類型不同愈腾,則編寫一種特殊的函數(shù),也就是所謂的可變參數(shù)模板岂津。
6.3 返回類型和return語(yǔ)句
void類型函數(shù)隱式地執(zhí)行了return虱黄。void類型函數(shù)也能使用return expression;,不過(guò)此時(shí)的expression必須是另一個(gè)返回void的函數(shù)吮成,否則會(huì)產(chǎn)生編譯錯(cuò)誤橱乱。
調(diào)用一個(gè)返回引用的函數(shù)得到左值,其他返回類型得到右值粱甫。
c++11新標(biāo)準(zhǔn)規(guī)定泳叠,函數(shù)可以返回花括號(hào)包圍的值的列表。如vector<string> function(),返回值可以是{},{"xxx","yyy"}茶宵。若函數(shù)返回的是內(nèi)置類型危纫,則花括號(hào)包圍的列表最多包含一個(gè)值,而且該值所占空間不應(yīng)該大于目標(biāo)類型的空間。若返回的是類類型叶摄,由類本身定義初始值如何使用属韧。
如果想定義一個(gè)返回?cái)?shù)組指針的函數(shù),則數(shù)組維度必須跟在函數(shù)名字之后蛤吓。函數(shù)的形參列表也跟在函數(shù)名字后面且形參列表應(yīng)該先于數(shù)組的維度宵喂。如:int (*func(int i))[10];? 要從里到外逐層理解其含義。
在C++11新標(biāo)準(zhǔn)中会傲,還有一種簡(jiǎn)化上述func聲明的方法锅棕,就是用尾置返回類型。如:auto func(int i) -> int(*)[10];
P206的三個(gè)練習(xí)
6.4 函數(shù)重載
如果同一作用域內(nèi)的幾個(gè)函數(shù)名字相同但形參列表不同淌山,稱之為重載函數(shù)裸燎。
重載主要是由編譯器根據(jù)傳遞的實(shí)參類型推斷想要的是哪個(gè)函數(shù)。
main函數(shù)不能重載泼疑。
一個(gè)擁有頂層const的形參無(wú)法和另一個(gè)沒有頂層const的形參區(qū)分開來(lái)德绿。
如果形參是某種類型的指針或引用,則通過(guò)區(qū)分其指向的是常量對(duì)象還是非常量對(duì)象可以實(shí)現(xiàn)函數(shù)重載退渗,此時(shí)的const是底層的移稳。
建議:最好只重載那些確實(shí)非常相似的操作。有些情況下会油,給函數(shù)起不同的名字能使得程序更易理解个粱。
函數(shù)匹配是指一個(gè)過(guò)程,在這個(gè)過(guò)程中把函數(shù)調(diào)用與一組重載函數(shù)中的某一個(gè)關(guān)聯(lián)起來(lái)翻翩,函數(shù)匹配也叫做重載確定都许。
如果在內(nèi)層作用域中聲明名字,它將隱藏外層作用域中聲明的同名實(shí)體嫂冻。在不同的作用域中無(wú)法重載函數(shù)名胶征。一旦在當(dāng)前作用域中找到了所需的名字,編譯器就會(huì)忽略掉外層作用域中的同名實(shí)體絮吵。
在C++中弧烤,名字查找發(fā)生在類型檢查之前。
6.5 特殊用途語(yǔ)言特性
6.5.1 默認(rèn)實(shí)參
一旦某個(gè)形參被賦予了默認(rèn)值蹬敲,它后面的所有形參都必須有默認(rèn)值暇昂。
函數(shù)聲明通常放在頭文件中,并且一個(gè)函數(shù)只聲明一次伴嗡,但是多次聲明同一個(gè)函數(shù)也是合法的急波。但要注意在給定的作用域中一個(gè)形參只能被賦予一次默認(rèn)實(shí)參。換句話說(shuō)瘪校,函數(shù)后續(xù)聲明只能為之前那些沒有默認(rèn)值的形參添加默認(rèn)實(shí)參澄暮,而且該形參右側(cè)的所有形參必須都有默認(rèn)值名段。
6.5.2 內(nèi)聯(lián)函數(shù)和constexpr函數(shù)
在大多數(shù)機(jī)器上,一次函數(shù)調(diào)用包含著一系列工作:調(diào)用前要先保存寄存器泣懊,并在返回時(shí)恢復(fù)伸辟;可能需要拷貝實(shí)參;程序轉(zhuǎn)向一個(gè)新的位置繼續(xù)執(zhí)行馍刮。
將函數(shù)指定為內(nèi)斂函數(shù)信夫,通常就是將它在每個(gè)調(diào)用點(diǎn)上“內(nèi)聯(lián)地”展開。
一般來(lái)說(shuō)卡啰,內(nèi)聯(lián)機(jī)制用于優(yōu)化規(guī)模較小静稻、流程直接、頻繁調(diào)用的函數(shù)匈辱。
constexpr函數(shù)是指能用于常量表達(dá)式的函數(shù)振湾。有如下約定:函數(shù)的返回類型及所有形參的類型都得是字面值類型,而且函數(shù)體必須有且只有一條return語(yǔ)句亡脸。
6.5.3 調(diào)試幫助
assert是一種預(yù)處理宏押搪。所謂預(yù)處理宏其實(shí)是一個(gè)預(yù)處理變量,它的行為有點(diǎn)類似于內(nèi)聯(lián)函數(shù)梗掰。 預(yù)處理名字由預(yù)處理器而非編譯器管理嵌言,所以可以直接使用該名字而無(wú)須提供using聲明。
assert(expr);? 首先對(duì)expr求值及穗,如果表達(dá)式為假(即0),assert輸出信息并終止程序的執(zhí)行绵载。如果表達(dá)式為真(即非0)埂陆,assert什么也不做。常用于檢查“不能發(fā)生”的條件娃豹。
assert宏定義在cassert頭文件中焚虱。宏名字在程序內(nèi)必須唯一。
NDEBUG預(yù)處理變量懂版,如果定義了NDEBUG鹃栽,則assert什么也不做∏耄可以使用一個(gè)#define語(yǔ)句定義NDEBUG民鼓,從而關(guān)閉調(diào)試狀態(tài)。定義NDEBUG能避免檢查各種條件所需的運(yùn)行時(shí)開銷蓬抄。
可把a(bǔ)ssert當(dāng)成調(diào)試程序的一種輔助手段丰嘉,但是不能用它替代真正的運(yùn)行時(shí)邏輯檢查,也不能替代程序本身應(yīng)該包含的錯(cuò)誤檢查嚷缭。
C++編譯器定義了幾個(gè)對(duì)于程序調(diào)試很有用的名字:__func__饮亏、__FILE__耍贾、__LINE__、__TIME__路幸、__DATE__荐开。
6.6 函數(shù)匹配
先選出候選函數(shù)(所有同名重載函數(shù),且其聲明在調(diào)用點(diǎn)可見)->再選出可行函數(shù)(實(shí)參類型是否與形參匹配)->尋找最佳匹配(如果有的話)简肴。
含有多個(gè)形參的函數(shù)匹配誓焦,如果在尋找最佳匹配時(shí)出現(xiàn)二義性,編譯器就會(huì)拒絕這個(gè)調(diào)用請(qǐng)求着帽。
6.7 函數(shù)指針
函數(shù)指針指向的時(shí)函數(shù)而非對(duì)象杂伟。
當(dāng)我們把函數(shù)名作為一個(gè)值使用時(shí),該函數(shù)自動(dòng)地轉(zhuǎn)換成指針(類似數(shù)組)仍翰。如func是函數(shù)名,pf = func;與pf = &func;是等價(jià)的赫粥。
此外,還能直接使用指向函數(shù)的指針調(diào)用該函數(shù)予借,無(wú)須提前解引用指針越平。
雖然不能定義函數(shù)類型的形參,但是形參可以是指向函數(shù)的指針灵迫。
秦叛。。瀑粥。