函數是一個命名了的代碼塊,可有0個或多個參數。
6.1 函數基礎
局部對象
- 自動對象: 存在于塊執行期間,當塊執行完畢時,則內存消失。例如函數的形參。
- 局部靜態對象: 生命周期為第一次定義到程序結束才終止。
函數聲明
函數聲明應該放到頭文件中。 這樣做的好處在于:同一函數的所有聲明一致;想改變函數的接口,只需要改變函數的聲明的即可。
6.2 參數傳遞
引用傳遞: 形參的類型決定了形參和實參的交互方式。 當形參是引用類型時,如其他引用一樣,引用形參是對應實參的別名。
值傳遞: 將實參的值拷貝給形參,此時形參和實參是兩個相互獨立的對象。對形參的改變,不會影響到實參的值。
6.2.1 值傳遞
*指針形參
void reset(int *ip)
{
*ip=0;
ip=0;
}
int i=42;
reset(&i)
此時形參和實參是兩個相互獨立的對象。
實現兩個數交換的兩種方法(C++中,建議使用引用類型代替指針形參)
void swap1(int *a,int * b)//指針 參數是兩個指針變量
{
int tmp;
tmp=*a; //把a指向的值賦給tmp
*a=*b; //把b指向的值賦給a指向的值
*b=tmp; //把tmp的值賦給b指向的值
//這樣就達到了變換a,b指向的值的目的
}
void swap2(int &a,int &b)//引用 參數是兩個整型變量的引用
{
//引用就是他本身的值,所以直接交換兩個的值就行了。
int tmp;
tmp=a;
a=b;
b=tmp;
}
6.2.2 引用傳遞
使用引用避免拷貝
當拷貝大的類類型對象或容器對象時,比較低效。
使引用可返回額外信息
一個return只能返回一個參數,通過引用帶出多個參數。
6.2.3 const形參和實參
void fcn(const int i){};
void fcn(int i){};
上面這兩個函數是一樣的,不能重載。因為當形參有頂層const時,傳給它的是const或非const均可以。
頂層const:指針本身是一個常亮;
底層const:指針所指的對象是一個常量。
const int ci=42; //頂層const
指針或引用形參與const
非常量可以初始化一個底層const對象,反之則不行;
同時,一個普通類型的引用,只能用同類型的對象初始化。
盡量使用常量引用
頂層const作為形參時,實參傳遞是const或非const均可;
例如:
bool is_empty(string &s)
{
return s.empty();
}
這種情況下,限制了該函數所能接受的實參類型,無法把const、對
象、字面值常量或者需要類型轉換的對象傳遞給普通的引用形參。
另一方面,也可能帶來誤導,可修改字符串s。
應該把形參改為 const string &s
6.2.4 數組形參
數組的兩個特性:不允許拷貝數組、數組名轉換為指針
6.2.5 main:處理命令行選項
int main(int argc, char *argv[])
{
//其中argc是argv[][]的行數
}
6.2.6 含有可變形參的函數---initializer_lsit
initializer_lsit是一種標準庫類型。適用于函數的實參未知,但類型都相同的情況下。
int icount(initializer_lsit<int> il)
{
int count =0;
for(auto val :il)
{
count += val;
}
return count;
}
icount({1,2,3,4,5});
6.3 返回類型和return語句
6.3.1 無返回值函數
函數的返回類型是void時,如果函數末尾沒有return時,也可以。因為在函數的最后一句會隱式的調用return。
6.3.2 有返回值函數
return exp;
不要返回局部對象的引用或指針
函數終止后,局部變量的引用將不再指向有效的內存區域。
引用返回左值
char &get_val(string &str, int index)
{
return str[index];
}
void main
{
string s("a value");
get_val(s,0) = 'A'; //改為 A value
}
6.3.3 返回數組的指針
int (*func(int i))[10];
6.4 函數重載
重載函數:名字相同,但是函數參數列表不同。
注:返回類型不算。
重載和const形參
頂層const
(因為當形參有頂層const時,傳給它的是const或非const均可以)。
(因為當形參有頂層const時,傳給它的是const或非const均可以),所以下面兩組函數是重復聲明。
Record lookup(phone);
Record lookup(const phone);
Record lookup(phone*);
Record lookup(phone* const );
形參是指針或者引用。將是底層const,可通過指向的對象時常量對象還是非常量對象實現重載。
一方面,const不能轉換為其他類型,只能將const轉換為const;另一方面,相反的,非const可以轉換為const,但是當同時存在非常量和const兩個版本的形參時,編譯器會優先選擇非常量版本的函數,
Record lookup(Account &);
Record lookup(const Account &);
Record lookup(phone*);
Record lookup(phone* const );
6.5 特殊用途語言特性
6.5.1 默認實參
規定:一旦某個形參被賦予了默認值,那么它后面的所有形參都必須被賦予默認值。
6.5.2 內聯函數和constexpr函數
將函數指定為內聯函數,通常就是在每個調用點進行展開。
constexpr函數---暫時用不到。
6.5.3 調試幫助
assert預處理
如:assert(expr),如果表達式為真,什么都不做;如果表達式為假,則輸出信息并終止程序。
DEBUG 預處理命令
如果你把代碼夾在#ifdef DEBUG 和對應的 #endif 中間,那么這段代碼只有在調試(DEBUG)下才會被編譯。也就是說,如果你在RELEASE模式下,這些代碼根本就不會存在于你的最終代碼里頭。
#include <iostream>
using namespace std;
#ifdef DEBUG
inline void msg()
{
cout<<"I'm testing";
}
#else
inline void msg() {}
#endif
int main()
{
msg();
return 0;
}
6.6 函數匹配
背景:有幾個重載函數,當一個調用函數調用重載函數時,可能會發生二義性。
解決辦法:尋求最佳匹配。
6.7 函數指針
bool lengthCompare(const string &,const string &);
- 該函數的函數類型為bool lengthCompare(const string &,const string &)。 函數類型,是由形參列表和返回值決定,函數名稱只是函數的別名而已。
- 聲明一個函數指針,只需要使用指針代替函數即可(因為函數名稱僅僅是一個別名)
bool (*pf) (const string &,const string &)
- 如何使用函數指針
當把函數名作為一個值使用時,該函數會自動的轉換為指針。
指向不同的函數指針之間,是不能轉換的,因為代表的函數返回值。
pf = lengthCompare; // 指向名為lengthCompare的函數
pf = &lengthCompare; //等價,取址操作符是可選的。
//直接使用
bool b1 = pf("hello", "world");
bool b2 = (*pf) ("hello", "world"); //等價調用
- 函數指針形參
//第三個形參是函數類型,自動轉換為指向函數的指針
void useBigger(const string &, const string &, bool pf (const string &,const string &));
|
void useBigger(const string &, const string &, bool (*pf) (const string &,const string &)); //自動轉換為函數指針
如上面,這樣顯得函數冗長。
通常:
typedef bool Func(const string &, const string &) ;
void useBigger(const string &, const string &, Func));