C++ Primer 第六章 函數 6.3~6.4 練習和總結

6.3 返回類型和return語句

返回值類型分爲兩種,一種是有返回值類型,還有一直是無返回值類型,

無返回值類型的函數,可以只寫一個return;也可以不寫return

有返回值類型的函數,必須返回一個值,該值爲返回的類型或者可以隱式的轉化爲返回值類型

無返回值

return;

有返回值

return experssion;//返回一個表達式,表達式由一個或者多個求值對象組成。

值是如何被返回的?

返回的值會在調用點初始化一個臨時量,這個臨時量就是函數調用的結果。

返回值類型可以是引用類型也可以是指針類型。需要注意的是,不要返回局部變量的引用以及指針類型。因爲局部變量在函數調用結束之後,聲明週期就結束了,內存被回收,所以返回局部變量的引用或者指針將產生未定義行爲

int & f(){
	int num=1;
	return num;
}

引用返回左值

返回值類型爲引用的話,返回的是左值,既然是左值就擁有左值的屬性,如果返回的是一個非常量的引用,那麼函數可以放在賦值語句的左邊


char& get_value(string &str,string::size_type index){
	return str[index];	
}

string str="123";
get_value(str,1)='c';
cout<<str<<endl;
>>> 1c3

看起來有點彆扭,但是試想以下,如果是類的成員函數這樣使用,看起來就正常很多了

String str="123";
str.get_value(1)='c';

這樣看是不是正常很多

列表初始化返回值

可以使用初始化列表對調用點的臨時變量進行初始化。

這裏我的理解是,調用點的臨時變量的類型爲返回值的類型,然後返回值類型使用初始化列表進行初始化,這樣一來就和平時用初始化列表初始化變量的形式一樣了。

如果是空列表{},則臨時變量使用值初始化。

內置類型初始化列表中只能由一個值

類類型的列表初始化,由類類型自己決定。

main函數的返回值

main函數可以不寫返回值,如果不寫的話,默認就是return 0;return 0表示程序執行成功,如果return 其他的值,含義則和具體的機器有關。

函數可以在函數體中調用自己,無論是直接調用還是間接調用,這樣調用的方式叫做遞歸。。遞歸最終要有終止條件,不然遞歸會一直進行下去直到調用棧滿了,然後報錯。

練習

6.30

bool str_subrange(const string& str1,const string &str2) {
	if (str1.size()==str2.size()) {
		return str1 == str2;
	}
	auto size = str1.size() < str2.size() ? str1.size() : str2.size();
	for (decltype(size) i = 0; i < size;++i ) {
		if (str1[i]!=str2[i]) {
			return false;//這裏會報錯,提示沒有返回值
		}
	}
	//return false;這裏不寫只會提示警告
}

6.31
當返回的是局部變量的引用時,返回的引用是無效的。

同樣返回的是局部變量的常量引用是無效的。

6.32
是正確的,該函數返回數組,對應index下標所對應的元素的引用

6.33


void print(vector<int>::iterator beg,vector<int>::iterator end) {
	if (beg==end) {//要有終止條件
		return;
	}
	else {
		cout<<*beg<<endl;
		print(beg+1,end);
	}
}

6.34

如果把終止條件加上 if(val!=0)

if factorial(int val){
	if(val>1){
	return factorial(val-1)*val;
}
if(val!=1){
return 1;
}

可見,當val等於1
的時候,並沒有對應的return語句。 在vs2017中會編譯器會發出警告,執行之後,得到一個不可預料的值。

6.35

因爲val–,返回的是val-1之前的副本,這樣程序永遠都不會結束。

6.3.3 返回數組指針

之前說了,形參數組,返回值也可以是數組。但是數組是不能夠拷貝的,而且數組在使用時變成了指向其列表第一個元素的指針,所以我們不能直接返回數組,但是可以返回數組的引用 或者指針。

有四種方式可以返回數組的指針或者引用

1.最原始的方法,這個方法和聲明數組引用很像。
只不過變量名變成了函數名()

string (&get_string_arr())[10] {

}

2.使用類型別名

using string_arr_10 = string[10];
string_arr_10& get_string_arr() {

}

這種方式非常的直觀,可讀性也很高

3.尾置返回類型

auto get_string_arr()->string(&)[10]{
	
}

在平時填返回值類型的地方,使用auto關鍵字,使用->來寫上真的返回類型。

這種方式看起來也非常的直觀,直到返回的就是大小爲10的string數組的引用。不需要額外語句。

4.使用decltype

string arr[10];
decltype(arr)& get_string_arr() {
}

這種方式需要先定義一個變量,我覺得最不好用。。

下面這種方式看起來也是很清晰,然而這種方式不錯誤的,編譯不了。

string(&)[10] get_string_arr() {
}

練習
6.36
已經寫在上面了

6.37
尾置返回類型,因爲尾置返回類型不需要額外的定義語句,而且可讀性很高

6.38
把*換成&就可以了。

函數重載

什麼是函數重載,C++ Primer中說的很明白,如果同一個作用域內的幾個函數名字相同但是形參列表不同,我們稱之爲重載函數。

所以下面的類型都是重載函數

int func(int i);
int func(double i);
int func(string i);

注意,main函數不能重載,同時main函數也不也能遞歸調用。

我們定義的多個重載函數,在調用時由實參的類型來確定,要注意的是,某些函數我們認爲是重載的,但是其實它們就是同一個函數

1.返回值類型不同,不是重載函數,這個從重載函數的定義就可以看到,定義中說的是形參列表不同,函數名相同

double func(int i);
int func(int i);

2.變量名字不同,不是重載函數 ,這些定義的其實是一個函數。形參的名字並不作爲重載函數的判斷條件。

int func(int);
int func(int a);
int func(int b);

3.類型別名,定義的形參列表和原來的類型不構成重載函數,INT本質上還是int,所以不構成重載函數。

using INT = int;
int func(INT i);
int func(int i);

4.形參是頂層const的形參(不是複合類型)無法和非const形參(不是複合類型)區分開來,不是重載函數。

int func(const int i);
int func(inti i);

int fun(int * i);
int func(int * const i)

以上的兩種情況都不是重載函數。

5.形參如果是某種類型的引用或者指針,定義了const的函數和沒有定義const的函數可以區分開來,構成重載函數。

int func(int& i);
int func(const int& i);

int func(int* i);
int func(const int* i);

複合類型的const和非const形參還是可以區分開來的。

但是在調用的時候,實參如果是非const,那麼會優先調用非const的函數。

之前在介紹函數的返回值類型時,寫了函數可以返回引用。但是有這樣一個情況。

const string& get_short_string(const string &s1,const string &s2){
return s1.size()<=s2.size()?s1:s2;
}

上面這個函數獲取較短的字符串,但是返回的結果爲const string& 類型,這就意味着無法修改它內部的值(當然使用const_cast 可以修改,但是這意味着我們的程序設計有問題)。

所以爲了滿足可以修改其內部的值,我們可以定義一個函數

string & get_short_string(string &s1,string &s2){
	auto &r = get_short_string(const_cast<const string&>(s1),const_cast<const string7>(s2));
	return const_cast<string&>(r);
}

使用這個函數得到的string類型是可以修改的,這樣如果我們傳入的類型是非常量的類型,那麼調用的就是這個函數,返回的類型可以修改,如果我們傳入的實參是常量類型,那麼調用的就是返回常量的版本

調用函數的重載

我們定義了多個重載函數,那麼編譯器該怎麼決定調用哪一個呢。
之前說了是按照實參的類型和數量來確定。

但是有些時候,不同函數的形參類型可以相互轉化時就不太好確定了,這裏怎麼確定還沒看

目前需要i記住的是。
1。編譯器會選擇一個實參最匹配的函數來調用
2.如果能夠匹配多個函數,則編譯器報錯
3.如果沒有任何一個函數可以匹配,則編譯器報錯

練習

a。兩個函數的形參不構成重載
b。返回值類型不同,不構成重載
c。沒問題

6.4.1 重載和作用域

函數的聲明和定義一般都會定義在所有的作用域之外,但是也可以定義在局部作用域內。

和之前變量在局部作用域中的屬性一樣,局部作用域中的變量會屏蔽到外層的同名函數。

int func(double);
int func(string)
int main(){
	int func(int);
	func(123);//調用int
	func(123.3);//double轉化爲int
	func("123");//報錯
}


int main(){
int func;
func(1231);
}

上面的代碼中,局部定義域中聲明瞭函數func,屏蔽了外部定義的函數。所以func(“123“);會報錯

同樣,如果代碼中有變量的名字和函數的名字一樣,變量的名字也會屏蔽掉函數的名字。

發佈了46 篇原創文章 · 獲贊 6 · 訪問量 3110
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章