第六章:函數

一:函數基礎

        形參和函數內部定義的變量統稱爲局部變量

        局部靜態對象使用static關鍵字,當程序執行到變量定義處生成該變量並且初始化,直到程序結束才被銷燬,在此期間即使變量所在的函數調用結束該變量也還在。例如下圖的ctr就是定義的局部靜態變量。

  

        函數聲明也稱爲函數原型,描述了函數的三要素:返回類型函數名形參類型

二:參數傳遞

        在c語言中,訪問函數外部對象用指針傳遞,在c++中建議使用引用傳遞。

        當函數無需修改形參的值時候,建議使用常量引用傳遞。

        如果形參不需要改變實參的值,就將其設爲常量引用,因爲普通引用可能會有修改實參的風險並且普通引用做形參會使得字面值常量和const類型對象無法做實參調用該函數,大大的限制了函數的使用。

三:數組形參

        1.使用結束標記,如下圖的代碼所示

        2.使用迭代器,如下圖所示。調用時候,print(begin(a), end(a)),這裏a是數組。

        3.把數組大小作爲參數傳遞進去。

四:main函數的參數

        main函數的帶參數形式如下,argv是一個數組,每個元素指向一個c風格字符串。argc表示數組中字符串的數量。

int main(int argc, char *argv[])

        假設prog文件中有main函數,在命令行執行“prog -d -o ofile data0”,則argc的值爲5,argv的內容如下圖所示:

五:含有可變形參的函數。

        使用initializer__list類型。具體應用如下面的代碼所示。和vector類型很像,不過也有區別,具體區別見以下博客https://www.jianshu.com/p/3d69ff89a0c9     c++的列表是initializer_list類型的,該類型不能修改元素。可以用數組和vector初始化inistializer_list類型。例如initializer_list<int> li(vi.begin(), bi.end()),這裏vi是vector<int>類型。

#include<iostream>
#include <initializer_list>
#include<vector>
using namespace std;
int sum(initializer_list<int> li)
{
	int sum = 0;
	for (auto &i : li)
	{
		sum += i;
	}
	return sum;
}
int main(int argc, char *argv[])
{
	initializer_list<int> li = { 1,2,3,4,5 };
	int k = sum(li);
	cout << k << endl;
	while (1) {}
	return 0;
}

六:函數返回左值

        當函數返回的是引用類型的時候,返回的是左值。這時候如果返回的是常量引用類型,可以用於賦值語句。如下面代碼所示,程序將輸出hEllo,dear。

#include<iostream>
#include <string>
using namespace std;
char &get_val(string &s, int index)
{
	return s[index];
}
int main(int argc, char *argv[])
{
	string s = "hello,dear";
	get_val(s, 2) = 'E';
	cout << s << endl;
	return 0;
}

七:返回指向數組的指針

        可以用 int *fun()[10]返回一個指針,該指針指向大小爲10的數組。也可以用尾置返回類型auto fun() -> int (*)[10]。

八:函數重載

        當兩個函數可以接受相同的實參時候就不是重載了。所以頂層const修飾的變量不是重載,例如下圖的四個函數的定義。

因爲無論輸入的實參是常量還是非常量,都沒法區分開該調用哪個函數。

 

        但是如果是底層const,就可以實現重載的。如下圖所示的函數定義,當傳入一個指向常量的指針或者引用的時候,就會找到形參是const版本的函數,另一個非const版本是沒法接受指向常量的指針或引用作爲實參的。當傳入一個指向非常量的指針或者引用時候,兩個版本的函數的形參都能接受實參,不過會優先選擇非const的版本。

        在內層作用於定義同名函數會隱藏外層的函數。如下圖內層的print會覆蓋外層的print函數。

九:默認參數

#include<iostream>
#include <string>
using namespace std;
int n_para = 0;
string s_para = "before_change";
void print(int n = n_para, string s = s_para)
{
	cout << n << " " << s << endl;
}
int main(int argc, char *argv[])
{
	print();
	s_para = "after_change";
	n_para = 10;
	print();
	int n_para = 100;
	print();
	while (1) {}
	return 0;
	
}

 

輸入如下

0 before_change
10 after_change
10 after_change

        在主函數裏面修改s_para和n_para後,調用函數時候,才具體求值然後給形參初始化,所以第二次調用輸出10, after_change。但是在主函數中重新定義同名變量n_para後,隱藏了原來的n_para,但是函數用到的實參是原來定義的n_para,所以第三次調用輸出仍然是10而不是100;

十:匹配函數

        一:選出候選函數:1.與被調用的函數同名。2.在調用點函數可見。

        二:選出可行函數:1.形參數量與提供的實參數量相等。2.每個實參的類型與對應的形參類型相同,或者能夠通過類型轉換轉化爲形參類型。

        三:尋找最佳匹配:若函數滿足1.該函數每個實參的匹配都不劣於其他可行函數的匹配。2.該函數至少有一個實參的匹配優於其他可行函數的匹配。例如對下面所示的代碼,編譯會報錯:有多個重載有相似的轉換。

#include<iostream>
using namespace std;
void f(int a, int b)
{
	cout << a + b << endl;
}
void f(double a, double b)
{
	cout << a - b << endl;
}
int main(int argc, char *argv[])
{
	f(1.3, 1);	
	return 0;	
}

十一:函數指針

        一.函數類型:函數類型由他的返回類型和形參類型決定,與函數名無關。例如bool lengthCompare(const string &s1, const string &s2),該函數的類型是bool (const string &, const string &)。

        二.定義一個函數指針的語句爲 bool (*p)(const string &, const string &)。

        三.爲函數指針賦值的語句可以是以下兩種: 這裏,取地址符可選。

    p = lengthCompare;
	p = &lengthCompare;

        四.通過函數指針調用調用函數可以是以下兩種。

	bool b = p("hello", "hi");
	bool c = (*p)("hello", "hi");

        五.函數指針可以作爲函數的形參。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章