c++從入門到放棄(五)函式基礎
阿新 • • 發佈:2019-12-31
坑!
5.1 引數
int f(i++,i)
//預設是不清楚先計算i還是i++的,沒有規定實參的求值順序,但是,可以假設從後往前計算,c語言中是這個過程
int f(int a,int)
//形參名是可選的,一般情況下需要一個名字,不提供也行,就是找不到這個形參
int f()
{
static int a=0;
//這個區域性靜態物件的生命週期是一直到整個程式結束!程式!
//未顯式初始化則預設初始化為0(內建型別)
}
int f(int,int);
//宣告裡面不一定需要形參名,注意是宣告!
//函式三要素:返回型別(不寫其實預設為int),函式名,形參型別
int f(int &a,int &b)
//傳引用,可修改
//但是不可以傳一個字面值,比如f(1,2),引用型別不可以初始化為字面值
int f(const int &a,const int &b)
//這就可以了。。。。f(1,2)沒有問題
//使用引用可以避免拷貝,特別大的字串的時候!
//傳遞陣列的時候,實際上傳遞的是陣列的指標
//多維陣列就不說了
//如果有問題,想想這樣初始化這個形參是否會出現問題
複製程式碼
如果不清楚形參的數量,可以使用省略符形參
void foo(int a,...)
void foo(...)
複製程式碼
5.2 返回值
在含有return語句的迴圈後面應該也有一條return語句,如果沒有的話就是錯誤的,如果編譯器沒有發現這個錯誤,則執行時的行為就是未定義的。
for(int i=0;i<n;i++)
if(i>8)
return;
return; //必須寫以防止最後不返回
複製程式碼
不要返回區域性物件的引用或者指標,區域性變數使用的空間在函式結束後是會被釋放的,所以不可以返回區域性變數的指標或者引用。但是返回區域性變數的值是可以的,因為使用的是拷貝的資料。
const string &manip()
{
string ret;
if(!ret.empty())
return ret; //錯誤,返回區域性變數的引用
else
return "abv"; //錯誤,這是一個區域性臨時量,在函式結束的時候一樣會被清空空間
}
複製程式碼
返回型別別或者引用,指標之類的函式,可以使用函式呼叫的結果訪問其成員
auto z=shorterString(s1,s2).size();
//返回一個引用型別的函式可以當做左值
char &get_val(string &s,int x)
{return s[x];}
int main()
{
get_val(s,x)=1; //直接當做左值修改
}
複製程式碼
也可以使用列表初始化返回
return {1,2,3} //一般用來返回容器型別
複製程式碼
5.3 過載
如果同一作用域內的幾個函式名字相同但是形參列表不同(形引數量或形參型別),則稱之為過載函式。
注意:main函式是不能過載的
int fun(int a,int b);
int fun(int a)
int fun(char c)
int fun(int c) //這就不是過載
int fun(int) //這也不是過載
複製程式碼
注意,函式引數是不認可頂層const的,所以預設頂層const加不加都一樣。
int fun(int a);
int fun(const int a); //不可以,頂層const
int fun(int *a);
int fun(int * const a); //不可以,頂層const
//如果形參是某種型別的指標或者引用,則通過區分其指向的是常量物件還是非常量物件可以實現函式過載,此時const是底層的
int fun(int &);
int fun(const int &); //可以過載,底層const
int fun(int *);
int fun(const int *); //可以過載,底層const
複製程式碼
注意需要避免函式過載的二義性!
在不同的作用域中無法過載函式!
5.4 特殊特性
可以為函式形參賦值預設實參(在宣告中賦值)
int fun(int a,int b,int c=1);
//可以為一個或者多個形參賦值預設值,但是,一旦一個形參賦值了,則後面的形參都必須有預設實參
int fun(int a,int b=1,int c); //無法分辨fun(1,2)的二義性
//注意無法修改一個已經存在的預設值
int fun(int,int,int c=2); //錯誤
//但是可以新增預設實參
int fun(int a,int b=2,int c=1); //正確
##函式呼叫時實參按其位置解析,預設實參負責填補函式呼叫缺少的尾部實參
int fun(string a="123",int a=1,int b=2);
fun(,2,3); //錯誤,預設補充尾部預設引數,想修改哪個那前面的都不能跳過!
//只能忽略尾部的實參
fun("1",2+1); //假如想修改b這個程式會和預期不符,所以要消除二義性
複製程式碼
行內函式,可以避免函式呼叫的開銷,不會進入到函式的地址區,而是通過編譯器直接將函式程式碼"貼上"到對應位置,所以不適用於大型的函式,只適用於小型的,頻繁呼叫的函式。
inline int f(int a,int b)
{return a>b?a:b;}
cout<<f(1,2)<<endl;
//等價於
cout<<1>2?1:2<<endl;
複製程式碼
assert預處理巨集
assert(expr)
//表示式為假則輸出資訊並終止程式的執行
//為真則什麼也不做
複製程式碼
5.5 函式匹配
具體操作如下
void f();
void f(int);
void f(int,int);
void f(double,double=3.14);
f(5.6);
(1)確定候選函式
1.與被呼叫的函式重名
2.其宣告在呼叫點可見
//無淘汰
(2)確定可行函式
1.形引數量與本次呼叫提供的實引數量相等(考慮預設引數可是可行的)
2.每個引數的型別與對應的形參型別相同,或者能轉換成形參型別
//淘汰第1,3
//如果這步未找到可行函式,編譯器會報無匹配函式錯誤
(3)尋找最佳匹配
尋找形參型別與實參型別最匹配的那個函式
最佳匹配等級:
1.精確匹配
實參型別和形參型別相同
實參從陣列型別或函式型別轉換成對應的指標型別
向實參新增頂層const或者從實參中刪除頂層const
2.通過const轉換實現匹配(底層const)
3.通過型別提升實現的匹配
4.通過算術型別轉換或指標轉換實現的匹配
5.通過型別別轉換實現的匹配
(如果過載函式的區別只是多了個底層const的話,將從函式呼叫的實際引數型別(常量或者非常量)來判斷)
多個形參的函式匹配
1.該函式每個實參的匹配都不劣於其他可行函式需要的匹配
2.至少有一個實參的匹配優於其他可行函式提供的匹配
如果檢查了之後沒有找到唯一的一個函式脫穎而出,則編譯器報告二義性呼叫的資訊。
//f(42,2.56)
//典型的二義性錯誤
複製程式碼
5.6 函式指標
函式指標指向的是函式不是物件。
int max(int a,int b);
//宣告一個可以指向該函式的指標,只需要用指標替換函式名即可
int (*p)(int,int);
//將函式賦值給函式指標的時候可以忽略取地址符
p=max;
p=&max; //加不加都一樣
//呼叫函式指標指向的函式的時候用不用解引用指標也一樣
cout<<p(1,2)<<endl;
cout<<(*p)(1,2)<<endl;
//可以為函式指標賦值0或者nullptr表示沒有指向任何位置
//當使用過載函式的時候,上下文必須清晰的界定到底使用的是哪個函式,不可以一對多
複製程式碼