前面幾章講的都是非常瑣碎的小事物,現在終於到了函數,所謂函數,就是一個命名的代碼塊,可以通過調用函數執行相應的代碼。
1 函數基礎
函數定義包括四部分:返回類型,函數名字,由0或多個形參組成的列表以及函數體。
通過調用運算符()來執行函數。函數調用的過程:首先是實參初始化函數對應的形參,接著將控制權轉移給被調函數,執行被調函數,當遇到return 語句時函數結束執行過程,首先返回return 語句中的值,接著將控制權從被調函數轉回主調函數。
返回類型。特殊的返回類型void ,不返回任何值。函數的返回類型不能是數組或函數類型,但可以是指向數組或函數的指針。
局部對象:形參和函數體內部定義的變量。
局部靜態對象:static 類型對象,在程序的執行路徑第一次經過對象定義語句時初始化,直到程序終止才銷毀。
函數聲明:也稱函數原型,函數聲明與函數的定義非常類似,唯一區別是函數聲明無須函數體,用一個分號代替。建議在頭文件中聲明,源文件中定義。
2 參數傳遞
參數的傳遞主要有兩種:傳值和傳引用。傳值就是將實參的值拷給形參,兩者相互獨立。傳引用時,形參是它對應實參的別名,是同一個對象。
傳值參數:函數對形參所做的所有操作都不會影響實參。對於指針形參,拷貝的是指針的值,此時兩個指針是不同的指針,可以修改指針指向對象的值,但是實參指針不會改變。C++ 建議使用引用類型的形參代替指針。
傳引用:對引用的操作實際是作用在引用所指的對象上。拷貝大的類類型對象或容器對象比較低效,所以應盡量避免直接拷貝他們,這時使用引用形參是好主意,如:
bool isShorter(const string &s1, const string &s2) { return si.size() < s2.size(); }
如果函數無須改變引用形參的值,最好將其聲明為常量const 引用。
一個函數只能返回一個值,如果需要同時返回多個值,引用形參可以實現,就是傳遞參數時傳入一個引用形參,不過它得在函數外部定義過。
當傳入的const 形參是頂層const 時,使用實參初始化時會忽略掉頂層const,如:
void fcn(const int i) void fcn(int i)
會出現錯誤,重定義函數。
數組形參。數組的兩個性質:不允許拷貝數組,使用數組時會轉換成指針,因此以下三個函數聲明等價:
void print(const int *); void print(const int []); void print(const int [10]);
因為數組是以指針形式傳給函數的,所以一開始函數不知道數組的確切尺寸,調用者需提供一些額外信息。管理指針形參的三種技術:
(1) 使用標記指定數組長度。C風格字符串最後跟一個空字符
void print(const char *cp) { if (cp) while (*cp) cout<<*cp++; }
(2) 使用標準庫規範,傳遞指向數組首元素和尾後元素的指針
void print(const int *beg, const int *end) { while (beg != end) cout<<*beg++<<endl; }
(3) 顯式傳遞一個表示數組大小的形參
void print(const int ia[], size_t size) { for (size_t i = 0; i != size; ++i) { cout<<ia[i]<<endl; } }
數組引用形參,如:
void print(int (&arr)[10]) { for (auto elem : arr) cout<<elem<<endl; }
傳遞多維數組,如:
void print(int (*matrix)[10],int rowsize) { }
main:處理命令行選項
int main(int argc, char *argv[])
第一個形參argc 表示數組中字符串的數量,第二個形參argv 是一個數組,它的元素是指向C風格字符串的指針。
使用argv中的實參時,要註意可選的實參從argv[1] 開始,第一個(argv[0])保存的是程序的名字。
有時我們無法預知應該向函數傳遞幾個實參,這時就需要處理可變實參的函數,兩種方法,如果所有實參類型相同,可以傳遞一個名為initializer_list的標準庫類型,如果實參類型不同,需要編寫一個可變參數模板。
3 返回類型與return 語句
return 語句終止當前正在執行的函數並將控制權返回到調用該函數的地方。
沒有返回值的return 語句只能出現在返回類型為void 的函數中。
有返回值的return 語句,返回值的類型必須與函數的返回類型相同,或者能隱式轉換成函數的返回類型。
對於正常的非引用的值會返回一個對象的副本或者未命名的臨時對象,而返回引用不會真正拷貝對象,所以返回引用效率更高一些。
函數終止意味著局部變量的引用或指針將指向不再有效的內存區域。因此不要返回局部對象的引用或指針。
4 函數重載
重載函數:同一作用域內的幾個函數名字相同但形參列表不同。
因為頂層const 不會影響傳入函數的對象,所以一個擁有頂層const 的形參無法和一個沒有頂層const 的形參區分開來。而如果形參是某種類型的指針或引用,則根據是否是常量對象可以實現函數重載,如:
void lookup(phone); void lookup(const phone); // 重復聲明lookup void lookup(Account &); void lookup(const Account &); // 重載
const_cast 與重載
現有函數定義如下:
const string &shorterString(const string &s1, const string &s2) { return s1.size() <= s2.size() ? s1 : s2; }
這個函數的參數和返回類型都是const string的引用,如果想要傳入非常量可以這樣:
string &shorterString(string &s1, string &s2) { auto &r = shorterString(const_cast<const string &>(s1), const_cast<const string &>(s2)); return const_cast<string &>(r); }
5 默認實參與inline函數
默認實參如:
typedef string::size_type sz; string screen(sz ht = 24, sz wid = 80, sz backgrnd = ' ');
函數調用的缺點:函數調用一般比求等價表達式的值要慢一點。而內聯函數可避免函數調用的開銷。
內聯機制用於優化規模較小,流程直接,頻繁調用的函數。
6 函數指針
函數指針指向的是函數而非對象。
聲明一個指向函數的指針,只要用指針替換函數名就好,如:
bool lengthCompare(const string &, const string &); bool (*pf)(const string &, const string &);
當把函數名作為一個值使用時,該函數會自動的轉換成指針。
pf = lengthCompare;
Tags: return 源文件 程序 命名 影響
文章來源: