- 大多數(shù)類型都能用作函數(shù)的返回類型,一種特殊的返回類型是void絮识,它表示函數(shù)不返回任何值。函數(shù)的返回類型不能是數(shù)組類型或函數(shù)類型嗽上,但可以是指向數(shù)組或函數(shù)的指針笋除。
- 在C++中,名字有作用域炸裆,對象有生命周期:
- 名字的作用域是程序文本的一部分,名字在其中可見
- 對象的生命周期是程序執(zhí)行過程中該對象存在的一段時間
- 對于普通局部變量對應(yīng)的對象來說鲜屏,當函數(shù)的控制路徑經(jīng)過變量定義語句時創(chuàng)建該對象烹看,當?shù)竭_定義所在的塊末尾時銷毀它。我們把只存在于塊執(zhí)行期間的對象稱為自動對象(automatic object)洛史。當塊的執(zhí)行結(jié)束后惯殊,塊中創(chuàng)建的自動對象的值就變成未定義的了。
- 某些時候也殖,有必要令局部變量的生命周期貫穿函數(shù)調(diào)用及之后的時間土思。可將局部變量定義成static類型從而獲得這樣的對象忆嗜。局部靜態(tài)對象(local static object)在程序執(zhí)行流第一次經(jīng)過對象定義語句時初始化己儒,并且知道程序終止時才被銷毀,即使對象所在的函數(shù)結(jié)束執(zhí)行也不會對它有影響捆毫。
5.和其他名字一樣闪湾,函數(shù)的名字也必須在使用之前聲明。類似于變量绩卤,函數(shù)只能定義一次途样,但可以聲明多次。函數(shù)的聲明和函數(shù)的定義非常類似濒憋,唯一的區(qū)別是函數(shù)聲明無須函數(shù)體何暇,用一個分號替代即可。函數(shù)的三要素(返回類型凛驮,函數(shù)名裆站,形參類型)描述了函數(shù)的接口,說明了調(diào)用該函數(shù)所需的全部信息辐烂,函數(shù)聲明也稱作函數(shù)原型(function prototype) - 函數(shù)應(yīng)該在頭文件中聲明而在源文件中定義遏插,同時定義函數(shù)的源文件應(yīng)該把含有函數(shù)聲明的頭文件包含進來,編譯器負責驗證函數(shù)的定義和聲明是否匹配纠修。
- 當形參是引用類型時胳嘲,我們說它對應(yīng)的實參被引用傳遞(passed by reference)。當實參的值被拷貝給形參時扣草,形參和實參是兩個相互獨立的對象了牛。我們說這樣的實參被值傳遞(passed by value)
- 指針的行為和其他非引用類型一樣颜屠,當執(zhí)行指針拷貝操作時,拷貝的是指針的值鹰祸,兩個指針是不同的指針甫窟。因為指針可以使我們間接地訪問它所指的對象,所以通過指針可以修改它所指對象的值蛙婴,但本質(zhì)上還是屬于值傳遞粗井。
- 在C++中,建議使用引用類型的形參替代指針來訪問外部對象街图。
- 拷貝大的類類型對象或者容器對象比較低效浇衬,甚至有的類類型(包括IO類型在內(nèi))根本不支持拷貝操作。這種情況下函數(shù)只能通過引用形參訪問該類型的對象餐济。
- 如果函數(shù)無需改變引用形參的值耘擂,最好將其聲明為常量引用。
- 一個函數(shù)只能返回一個值絮姆,然而有時函數(shù)需要同時返回多個值醉冤,引用形參為我們一次返回多個結(jié)果提供了有效的途徑。我們可以利用引用形參來保存需要返回的多個值篙悯。
- 當用實參初始化形參時會忽略掉形參的頂層const蚁阳,傳給它常量對象或者非常量對象都是可以的。
- 數(shù)組的兩個特殊性質(zhì)對我們定義和使用作用在數(shù)組上的函數(shù)有影響辕近,這兩個性質(zhì)分別是韵吨,不允許拷貝數(shù)組以及使用數(shù)組時通常會將其轉(zhuǎn)換成指針。所以當我們?yōu)楹瘮?shù)傳遞一個數(shù)組時移宅,實際上傳遞的是指向數(shù)組首元素的指針归粉。盡管不能以值傳遞的方式傳遞數(shù)組,但是我們可以形參寫成類似數(shù)組的形式:
- void print(const int*)
- void print(const int[])
- void print(const int[10]) 這里的維度只是我們期望的漏峰,實際上不一定
盡管表現(xiàn)形式不同糠悼,但上面的三個函數(shù)是等價的:每個函數(shù)的唯一實參都是const int*類型的。
- 因為數(shù)組是以指針的形式傳遞給函數(shù)的浅乔,所以一開始函數(shù)并不知道數(shù)組的確切尺寸倔喂,調(diào)用者應(yīng)該為此提供一些額外的信息。管理指針形參有三種常用的技術(shù):
- 使用結(jié)束標記:比如C風格字符串
- 使用標準庫規(guī)范:傳遞指向數(shù)組首元素和尾后元素的指針
- 傳遞一個表示數(shù)組大小的形參
- 當函數(shù)不需要對數(shù)組元素執(zhí)行寫操作時靖苇,數(shù)組形參應(yīng)該是指向const的指針
- C++允許將變量定義成數(shù)組的引用席噩,基于同樣的道理,形參也可以是數(shù)組的引用贤壁。此時悼枢,引用形參綁定到數(shù)組上。但此時函數(shù)只能作用于固定大小的數(shù)組脾拆。
//形參是數(shù)組的引用馒索,維度是類型的一部分
void print(int (&arr)[10]){
for (auto elem : arr)
cout<<elem<<endl;
}
- 當將多維數(shù)組傳遞給函數(shù)時莹妒,真正傳遞的是指向數(shù)組首元素的指針,所以首元素本身就是一個數(shù)組绰上。指針就是一個指向數(shù)組的指針旨怠,數(shù)組第二維(以及后面所有維度)的大小都是數(shù)組類型的一部分,不能省略蜈块。
void print(int (*matrix)[10]),int roSize);
上述聲明將matrix聲明成指向含有10個整數(shù)的數(shù)組的指針鉴腻。
- 命令行選項通過兩個(可選的)形參傳遞給main函數(shù):
int main(int argc,char *argv[]){...}
- 為了編寫能處理不同數(shù)量實參的函數(shù),C++11標準提供了兩種主要方法:如果所有實參類型相同百揭,可以傳遞一個initializer_list的標準庫類型拘哨;如果類型不同,我們可以編寫一種特殊的函數(shù)信峻,也就是所謂的可變參數(shù)模板。除此以外瓮床,還有一種省略符形參盹舞,不過一般用于與C函數(shù)交互的接口程序。
- 返回void的函數(shù)不要求非得有return語句隘庄,因為這類函數(shù)最后一句后面會隱含執(zhí)行return
- 在含有return語句的循環(huán)后面應(yīng)該也有一條return語句踢步,因為循環(huán)中的return不一定會被執(zhí)行到
- 函數(shù)返回的值用于初始化調(diào)用點的一個臨時量,該臨時量就是函數(shù)調(diào)用的結(jié)果丑掺。如果返回引用類型获印,則不需要拷貝。
- 必須要注意的是街州,千萬不要返回局部對象的引用或指針兼丰,因為函數(shù)完成后,它所占用的存儲空間也隨之被釋放掉唆缴,所以要想確保返回值安全鳍征,我們不妨提問:引用所引的是在函數(shù)之前已經(jīng)存在的哪個對象?
- C++11標準規(guī)定函數(shù)可以返回花括號包圍的值的列表(這與之前所述的列表初始化語法實際上相照應(yīng))面徽,比如用于返回一個vector<int>類型艳丛。如果返回內(nèi)置類型則花括號只能包含一個值。
- 之前介紹過趟紊,如果函數(shù)的返回值不是void氮双,那么它必須返回一個值。但這條規(guī)則有一個例外:我們允許main沒有return語句結(jié)束霎匈。如果控制流到達了main函數(shù)的結(jié)尾處而沒有return語句戴差,編譯器將隱式地插入一條返回0的return語句。
- 因為數(shù)組不能被拷貝唧躲,所以函數(shù)不能返回數(shù)組造挽,但是可以返回數(shù)組的指針和引用碱璃。使用類型別名可以簡化聲明過程。
typedef int arrT[10]; //arrT表示含有10個整數(shù)的數(shù)組
arrT* func(int i); //func返回一個指向含有10個整數(shù)的數(shù)組的指針
如果不使用類型別名饭入,返回數(shù)組指針的函數(shù)形式如下:
int (*func(int i)) [10];
這里的*表示函數(shù)func的返回結(jié)果可以執(zhí)行解引用操作嵌器。
- C++11標準中有一種可以簡化上述func聲明的方法,就是使用尾置返回類型(trailing return type)
auto func(int i) -> int (*)[10];
- 還有一種情況谐丢,如果我們知道函數(shù)返回的指針指向哪個數(shù)組爽航,就可以使用decltype關(guān)鍵字聲明返回類型:
int odd[]={1,3,5,7,9};
int even[]={0,2,4,6,8};
decltype(odd) *arrPtr(int i){
return (i%2) ? &odd : &even;
}
decltype并不負責把數(shù)組類型轉(zhuǎn)換成對應(yīng)的指針,所以decltype的結(jié)果是一個數(shù)組乾忱,要想表示返回指針還必須在聲明時加一個 *讥珍。
- 對于重載而言,頂層const不影響傳入函數(shù)的對象窄瘟。一個擁有頂層const的形參無法和另一個沒有頂層const的形參區(qū)分開來衷佃。
- 對于底層const,形參是某種類型的指針或引用蹄葱,則可以區(qū)分const氏义。即使非常量也能被底層const所指,但是編譯器會優(yōu)先非常量版本的函數(shù)图云。
- 可以將const_cast和重載搭配起來使用
- 當有多個重載函數(shù)可以匹配調(diào)用但每一個都不是明顯的最佳選擇時也將發(fā)生錯誤惯悠,稱為二義性調(diào)用(ambiguous call)。
- 在不同的作用域中無法重載函數(shù)名
- C++同樣支持默認參數(shù)竣况,默認參數(shù)必須在參數(shù)列表的最右邊
string screen(int hz=24,int wid=80,char backgrnd=' ');
- 在給定作用域中一個形參只能被賦予一次默認參數(shù)克婶,后續(xù)聲明智能給沒有默認值的形參添加默認實參。
- 只要表達式的值能轉(zhuǎn)換成形參所需的類型丹泉,該表達式就能作為默認實參情萤。
- 將函數(shù)指定為內(nèi)聯(lián)函數(shù)(inline),通常就是將它在調(diào)用點”內(nèi)聯(lián)地“展開摹恨。只需要在函數(shù)的返回類型前面加上關(guān)鍵字inline紫岩。內(nèi)聯(lián)函數(shù)一般用于優(yōu)化規(guī)模較小,流程直接睬塌,頻繁調(diào)用的函數(shù)泉蝌。
- constexpr函數(shù)是指能用于常量表達式的函數(shù),函數(shù)的返回值和所有形參類型都得是字面值類型揩晴。
- assert是一種預處理宏勋陪,assert宏用一個表達式作為它的條件:
assert(expr);
如果表達式為假,assert輸出信息并終止程序執(zhí)行硫兰;如果為真诅愚,assert什么也不做。
- assert的行為依賴于一個名為NDEBUG的預處理變量的狀態(tài)劫映。如果定義NDEBUG违孝,assert什么也不做刹前。默認情況下沒有定義NDEBUG,此時assert執(zhí)行運行時檢查雌桑。
- 函數(shù)指針指向的是函數(shù)而非對象喇喉。和其他指針一樣,函數(shù)指針指向某種特定類型校坑。函數(shù)的類型由它的返回類型和形參類型共同決定拣技,與函數(shù)名無關(guān)。要想聲明一個可以指向函數(shù)的指針耍目,只需要用指針替換函數(shù)名即可膏斤。
bool (*pf)(const string &,const string &);
- 當我們把函數(shù)名作為一個值使用時,該函數(shù)自動轉(zhuǎn)換成指針邪驮。此時取地址符是可選的莫辨。
- 我們還能直接使用指向函數(shù)的指針調(diào)用該函數(shù),無須提前解引用指針毅访。
- 指向不同函數(shù)類型的指針不存在轉(zhuǎn)換規(guī)則衔掸,但是我們可以和往常一樣為函數(shù)指針賦一個nullptr或者值為0的整型常量表達式,表示該指針沒有指向任何一個函數(shù)俺抽。
- 如果定義了指向重載函數(shù)的指針,指針類型必須與重載函數(shù)中的某一個精確匹配较曼。
- 和數(shù)組類似磷斧,雖然不能定義函數(shù)類型的形參,但是形參可以是指向函數(shù)的指針捷犹。此時弛饭,形參看起來是函數(shù)類型,實際上卻是當成指針使用:
void test(int pf(int)); //等價的聲明
void test(int (*pf)(int));
我們可以直接把函數(shù)名作為實參使用萍歉,此時它會自動轉(zhuǎn)換成指針侣颂。
- 類型別名和decltype能讓我們簡化聲明:
//Func和Func2是函數(shù)類型
typedef bool Func();
typedef decltype(test) Func2();
//FuncP和FuncP2是指向函數(shù)的指針
typedef bool (*FuncP)();
typedef decltype(test) *Func2();
因為decltype返回函數(shù)類型,所以要加上*才能得到指針枪孩。
- 和數(shù)組類似憔晒,雖然不能返回一個函數(shù),但是能返回指向函數(shù)類型的指針蔑舞。與往常一樣拒担,最簡單的方法是聲明一個類型別名,但此時返回類型不會自動轉(zhuǎn)換成指針攻询,我們必須顯式地將返回類型指定為指針从撼。
int (*f1(int)) (int *,int); //也能直接聲明一個函數(shù)指針返回值
我們還可以使用尾置返回類型的方式聲明一個返回函數(shù)指針的函數(shù):
auto f1(int)->int (*)(int*,int);
- 如果我們明確知道返回的函數(shù)哪一個,可以參考上文所述返回數(shù)組指針一樣使用decltype簡化書寫函數(shù)指針返回類型的過程钧栖。但記得要顯式加上*表示我們需要返回指針低零。