1. 程式人生 > 程式設計 >c++從入門到放棄(五)函式基礎

c++從入門到放棄(五)函式基礎

​ 坑!

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轉換實現匹配(底層const3.通過型別提升實現的匹配
			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表示沒有指向任何位置
//當使用過載函式的時候,上下文必須清晰的界定到底使用的是哪個函式,不可以一對多
複製程式碼