歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

c++ primer(第五版)學習筆記及習題答案代碼版(第六章)函數

筆記較為零散,都是自己不熟悉的知識點。

習題答案至於一個.cc 中,編譯需要包含Chapter6.h頭文件。 需要演示某一題直接修改 #define NUM***, 如運行6.23題為#define NUM623;

chapter 61、

形參初始化的機理與變量初始化一樣。

當形參是引用類型時,它對應的實參被引用傳遞或者函數被傳引用調用。

2、

const和實參

void fcn(const int i){ /*fcn能夠讀取i,但是不能向i寫值*/}

void fcn(int i){ /*....*/} //錯誤,重復定義了fcn(int)

c++中允許定義具有相同名字的函數,但是不同函數的形參列表應該有區別。這裡頂層const被忽略掉了,所以傳入的兩個fcn函數參數可以完全一樣。

3、

不能把const對象、字面值或者需要類型轉換的對象傳遞給普通的引用形參。盡量使用常量引用而非普通引用。

數組最為形參時,不允許拷貝數組以及使用數組時會將其轉換成指針。因為不能拷貝數組,所以我們

無法以值傳遞的方式使用數組參數。因為數組會被轉換成指針,所以當為函數傳遞一個數組時,實際上傳遞的

是指向數組首元素的指針。

數組形參的引用:

func(int (&arr)[10]);

4、

和vector一樣,initializer_list也是一種模板類型,定義initializer_list對象時,必須說明列表中所含元素的類型。

不要返回局部對象的引用或指針:

函數完成後,它所占用的存儲空間也隨之被釋放掉。函數終止意味著局部變量的引用將指向不再有效的內存區域。

5、

返回數組指針,最直接的方法就是使用類型別名

typedef int arrT[100];

using arrT = int[10];

arrT* func(int i);

6、

重載的函數應該在形參數量或形參類型上有所不同。不允許兩個函數除了返回類型外其他所有的要素都相同,否則報錯。

c++語言中,類型查找發生在類型檢查之前。

一旦在當前局部作用域中找到了所需的名字,編譯器就會忽略掉外層作用域中的同名實體,剩下的工作是檢查函數調用是否有效。

7、

局部變量不能作為默認實參。另外,只要表達式的類型能轉換成形參所需的類型,該表達式就能作為默認實參。

調用函數一般比求等價表達式的值要慢一些。

一次函數調用要包含一系列工作:調用前要先保存寄存器,並在返回時恢復;可能需要拷貝實參;程序轉向一個新的位置繼續執行。

內聯聲明只是向編譯器發出一個請求,編譯器可以選擇忽略這個請求。

constexpr函數是指能用於常量表達式的函數。定義constexpr要遵顼幾個約定:

函數的返回類型及所有形參的類型都得是字面值類型,而且函數體中必須有且只有一條return語句。

8、

assert是一種預處理宏,即是一個預處理變量,它的行為有點類似於內聯函數:

assert(expr);

先對expr求值,如果表達式為假,assert輸出信息並終止程序的執行。如果表達式為真,assert什麼也不做。

9、

NDEBUG預處理變量:

assert的行為依賴於一個名為NDEBUG的預處理變量的狀態。如果定義了NDEBUG,則assert什麼也不做。默認不定義

NDEBUG,將執行assert。

_ _FILE_ _ 存放文件名的字符串字面值

_ _LINE_ _ 存放當前行號的整型字面值

10、

函數指針指向的是函數而非對象。它指向某種類型類型,函數指針由它的返回類型和形參類型共同決定,與函數名無關。

bool lengthCompare(const string&, const string &);

bool (*pf)(const string &, const string &); //pf指向這個函數,該函數的參數是兩個const string的引用。

當我們把函數名作為一個值使用時,改函數自動地轉換成指針。

void useBright(const string &s1, const string &s2, bool pf(const string &, const string &));

等價於:pf作為形參自動轉換為指針

void useBright(const string &s1, const string &s2, bool (pf*)(const string &, const string &));

同樣的,聲明返回函數指針的函數,最簡單的方法就是使用類型別名。

#ifndef CHAPTER6_H
#define CHAPTER6_H
int fact(int val);

int func();

template <typename T> T abs(T val){
	return val >= 0 ? val : -val;
}

#endif
#include <iostream>
#include "Chapter6.h"
using namespace std;

int main(){
	cout << "5! is "<<fact(5)<<endl;
	cout << func() <<endl;
	cout << abs(-2.34) <<endl;
}
#include <iostream>
#include "Chapter6.h"
using namespace std;

int fact(int val){
	if(val == 0 || val ==1)
		return 1;
	else 
		return val * fact(val-1);
}

int func(){
	int n, ret =1;
	cout<< "Enter a number: "<<endl;
	cin >> n;
	while(n > 1)
		ret *= n--;
	return ret;
}
#include <iostream>
#include <vector>
#include <cstring>
#include <cassert>
#include "Chapter6.h"
#define NUM648
#define NDEBUG 
using namespace std;

/*6.3*/
int fact(int val){
	if(val == 0 || val ==1) 
		return 1;
	else
		return val * fact(val-1);
}
/*6.4*/
int fact2(){
	int val;
	cout << "Enter a number: "<<endl;
	while(cin >> val){
		if( val == 0 || val < 0)
			cout<<"Please input a positive number. "<<endl;
	cout<< val;
	unsigned long long exp = 1;
	exp = fact(val);
	cout<< "! is: ";
	if(exp) 
		cout << exp <<endl;
	else 
		cout<< "too big. "<<endl;
	}	
}
/*6.5*/
#ifdef NUM65
template <typename T>
T abs(T val){
	return val > 0 ? val : -val;
}
#endif
/*6.6*/
int temp(int val1){
	static int val2 = 0;
	val2 += val1;
	return val2;
}
/*6.7*/
int func(){
	static int flag = 0;
	return flag++;
}
/*6.10*/
void  swap(int *val1, int *val2){
	int temp;
	temp = *val1;
	*val1 = *val2;
	*val2 = temp;
	
}
/*6.11*/
void reset(int &val){
	val  = 0;
}
/*6.12*/
void swap2(int &val1, int &val2){
	int temp;
	temp = val1;
	val1 = val2;
	val2 = temp;
}
/*6.17*/
bool hasUpper(string &s){
	for(string::iterator it = s.begin(); it != s.end(); ++it){
		if(isupper(*it))
			return true;
		else 
			return false;
	}
}
void changTolower(string& s){
	for(size_t it = 0; it != s.size()-1; ++it){
		if(isupper(s[it])){
			s[it] = tolower(s[it]);
		}
	}
}
/*6.18*/
class matrix;
bool compare(matrix &ma1, matrix &ma2);
vector<int>::iterator change_val(int, vector<int>::iterator);
/*6.21*/
int contrast(const int val1, const int *p){
	return val1 > *p ? val1 : *p; 
}
/*6.22*/
void swap3(int*& val1, int*& val2){
	int* temp;
	temp = val1;
	val1 = val2;
	val2 = temp;
}
/*6.23*/
void print(const int *p){
	if(p)
		cout<< *p <<endl;
}
void print(int size, int str[]){
	for(size_t it = 0; it!= size; ++it)
		cout<< str[it] << endl;
}
void print(const int* beg, const int* end){
	for(; beg!= end; )
		cout<< *beg++ <<endl;
}
void print(int(&arr)[2]){
	for(int i =0; i<= 1; ++i)
		cout<< arr[i]<<endl;
}
/*6.27*/
#ifdef NUM627
int sum(initializer_list<int> li){
	int sum(0);
	for(initializer_list<int>::iterator beg = li.begin(); beg!= li.end(); ++beg)
		sum += *beg;
	return sum;
}
#endif
/*6.30*/
#ifdef NUM630
bool str_subrange(const string &str1, const string &str2){
	if(str1.size() == str2.size())
		return str1 == str2;
	size_t size = str1.size() < str2.size() ? str1.size() : str2.size();
	for(size_t i =0; i!= size; ++i){
		if(str1[i] != str2[i])
			return;
	}
}
#endif
/*6.33*/
int &get(vector<int> &ia, int index){ return ia[index]; }
void print(vector<int>::iterator beg, vector<int>::iterator end){
	if(beg != end){
		cout << *beg <<" ";
		print(next(beg), end);
	}
}
/*6.35*/
int factorial(int val){
	if(val > 1)
		return factorial(val -1) * val;
	return 1;
}
/*6.36*/
string (&func2(string (&str)[10]))[10];
/*6.37*/
#ifdef NUM637
typedef string arrT[10];
arrT &func3(arrT& str);

auto func4(arrT& str) -> string(&)[10];

string arrS[10];
decltype(arrS)& func5(arrT& str);
#endif
/*6.38*/
int odd[] = {1,3,5,7,9};
int even[] = {0,2,4,6,8};
typedef int arrInt[5];
arrInt&  arrPtr(int i){
	return (i % 2) ? odd : even; //返回引用
}
/*6.42*/
string make_plural(size_t ctr, const string& word, const string &ending = "s"){
	return (ctr > 1) ? word + ending : word;
}
/*6.44*/
inline bool isShorter(const string &s1, const string &s2){
	return s1.size() < s2.size();
}
#ifdef NUM646
/*6.46*/
constexpr bool isShorter1(const string &s1, const string &s2){
	return s1.size() < s2.size();
}
#endif
/*6.47*/
#ifdef NUM647
void printVector(vector<int>& vec){
#ifdef NDEBUG
	cout << "vector size: " << vec.size() <<endl;
#endif
	if(!vec.empty()){
		auto temp = vec.back();
		vec.pop_back();
		printVector(vec);
		cout << temp <<" ";
	}
}
#endif
/*6.51*/
#ifdef NUM651
void f(){
	cout << "f()"<<endl;
}	
void f(int){
	cout << "f(int)"<<endl;
}
void f(int, int){
	cout << "f(int, int)"<<endl;
}
void f(double, double = 3.14){
	cout << "f(duble, double)"<<endl;
}
#endif
/*6.54*/
typedef int func6(int, int);
vector<func6*> vec;
/*6.55*/
int add(int a, int b){
	return a + b;
}
int substact(int a, int b){
	return a - b;
}
int multiply(int a, int b){
	return a * b;
}
int divide(int a, int b){
	return b !=0 ? a/b : 0;
}

int main(){
/*6.1*/
#ifdef NUM61
	cout<<"實參是形參的初始值,實參的類型必須與對應的形參類型匹配。"<<endl;	
#endif
/*6.2*/
#ifdef NUM62
	cout<<"(a)返回類型不匹配,int型改成string;(b)沒有定義函數的返回類型,可以用void代替; (c)缺少一個括號; (d)函數體缺少一對括號."<<endl;
#endif
/*6.3*/
#ifdef NUM63
	cout << "5!: " << fact(5) <<endl;
#endif
/*6.4*/
#ifdef NUM64
	fact2();
#endif
/*6.5*/
#ifdef NUM66
	cout << "The absolute of -2.34 is: "<< abs(-2.34)<<endl;
#endif
/*6.6*/
#ifdef NUM66
	cout<< "形參的生命周期是從函數開始到函數終止即被銷毀,局部變量的生命周期是從其被創建到函數體結束。靜態局部變量在被初始化開始,直到程序結束才會被銷毀。"<<endl;
	for(int val3 =0; val3 !=10; ++val3)
		cout << "靜態變量與局部變量的和是: "<< temp(val3) <<endl;
#endif
/*6.7*/
#ifdef NUM67
	for(int i =0; i!=4; ++i)
		cout<< func()<<endl;
#endif
/*6.8*/
#ifdef NUM68
	cout<<"見Chapter6.h" <<endl;
#endif
/*6.9*/
#ifdef NUM69
	cout<<"見fact.cc factMain.cc" <<endl;
#endif
/*6.10*/
#ifdef NUM610
	int val1 = 1, val2 = 2;
	swap(&val1, &val2);
	cout<< "val1: "<< val1 << " val2: " << val2<<endl;
#endif
/*6.11*/
#ifdef NUM611
	int val = 23;
	reset(val);
	cout << "val has been reset: "<< val <<endl;
#endif
/*6.12*/
#ifdef NUM612
	for(int val1(0), val2(0); cout<< "Enter two numbers: \n", cin >> val1 >> val2;){
		swap2(val1, val2);
		cout<< "val1: "<< val1 << " val2: " << val2<<endl;
	}
#endif
/*6.13*/
#ifdef NUM613
	cout<<"第一種是傳值調用,調用過程中不會修改實參的值,第二種傳地址引用調用,在調用過程中將會與實參的綁定。"<<endl;
#endif
/*6.14*/
#ifdef NUM614
	cout<< "6.11例子中引用類型可以避免拷貝,但是在實參不希望被改變時不能使用引用形參."<<endl;
#endif
/*6.15*/
#ifdef NUM615
	cout<<"首先,實參s不能夠被改變,但是occur的最後值時通過函數計算的;c可能是一個臨時變量,可以換成其他值;如果交換類型,s可以被改變,occur不能改變,=0,報錯"<<endl;
#endif
/*6.16*/
#ifdef NUM616
	cout<< "應該設置為const引用,因為s不希望被改變,可以避免拷貝,直接使用字符串常量作為參數. "<<endl;
#endif
/*6.17*/
#ifdef NUM617
	string s = "C++ & Linux";
	if(hasUpper(s)) cout << "has upper letter. "<<endl;
	else			cout << "no upper letter. "<<endl;
	changTolower(s);
	cout << "After tolower: " << s <<endl;
#endif
/*6.18*/
#ifdef NUM618
	cout<<"見main函數外函數聲明;"<<endl;
#endif
/*6.19*/
#ifdef NUM619
	cout<< "(a)不合法,calc只有一個參數." <<endl;
#endif
/*6.20*/
#ifdef NUM620
	cout<< "一般能用const都加上const,如果設為普通引用,可能在函數中會改變常量的值."<<endl;
#endif
/*6.21*/
#ifdef NUM621
	int val1(2), val2(3);
	cout << "return the larger: " << contrast(val1, &val2) << endl;
#endif
/*6.22*/
#ifdef NUM622
	int val1(2), val2(3);
	int* p1 = &val1; int* p2 = &val2;
	swap3( p1, p2);
	cout << "val1: " << *p1 << " val2: " << *p2 << endl;
#endif
/*6.23*/
#ifdef NUM623
	int i =0, j[2] = {0, 1};
	print(&i);
	print(2, j);
	print(begin(j), end(j));
	print(j);
#endif
/*6.24*/
#ifdef NUM624
	cout<<"沒有什麼問題,單數如果僅僅為了遍歷數組,完全可以指針或引用的形式傳遞.如:"
	" void print(const int (&ia)[10]) "<<endl;
#endif
/*6.25*/
#ifdef NUM625
	cout<< "見main-6.25.cc"<<endl;
#endif
/*6.26*/
#ifdef NUM626
	cout<< "見main-6.26.cc"<<endl;
#endif
/*6.27*/
#ifdef NUM627
	cout << "Sum of 1-5: "<< sum( {1,2,3,4,5} ) <<endl;
#endif
/*6.28*/
#ifdef NUM628
	cout << "elem 的類型是const string&"<<endl;
#endif
/*6.29*/
#ifdef NUM629
	cout <<"因為initializer_list的元素總是 const類型,不能在函數內改變,應該聲明為常量引用類型. "<<endl;
#endif
/*6.30*/
#ifdef NUM630
	cout<<"見main函數外函數聲明;"<<endl;
#endif
/*6.31*/
#ifdef NUM631
	cout<< "返回局部變量的引用是無效的; 如果試圖對返回常量引用類型賦值,也是無效的."<<endl;
#endif
/*6.32*/
#ifdef NUM632
	cout<< "合法,作用是將0-9賦值給ia數組"<<endl;
#endif
/*6.33*/
#ifdef NUM633
	vector<int> vec(10,0);   //需要初始化,否則傳參時會出錯
	for(int i =0; i != 10; ++i)
		get(vec, i) = i;
	print(vec.begin(), vec.end());
	cout <<endl;
#endif
/*6.34*/
#ifdef NUM634
	cout <<"如果val為負數,將發生堆棧溢出. "<<endl;
#endif
/*6.35*/
#ifdef NUM635
	cout << factorial(5)<<endl;
	cout <<" val--無法遞歸,報錯 "<<endl;	
#endif
/*6.36*/
#ifdef NUM636
	cout<<"見main函數外函數聲明, 第一種聲明清晰,修改和閱讀比較方便. "<<endl;
#endif
/*6.37*/
#ifdef NUM637
	cout<<"見main函數外函數聲明;"<<endl;
#endif
/*6.38*/
#ifdef NUM638
	cout<<"見main函數外函數聲明;"<<endl;
#endif
/*6.39*/
#ifdef NUM639
	cout << "(a)非法,頂層const,重復聲明. (b)非法,函數參數相同. (c)合法. "<<endl;
#endif
/*6.40*/
#ifdef NUM640
	cout<< "(b)非法,一旦某個形參被賦予了默認值,它後面的所有形參都必須有默認值. "<<endl;
#endif
/*6.41*/
#ifdef NUM641
	cout<< "(a)非法,沒有給第一個形參傳值. (b)合法. (c)合法,we被賦值為'*',但與意圖不符. "<<endl;
#endif
/*6.42*/
#ifdef NUM642
	cout << "singual: " << make_plural(1, "success", "es")<<" "<< make_plural(1, "failure")<<endl;
	cout << "plural: " << make_plural(2,  "success", "es")<<" "<< make_plural(2, "failure")<<endl;
#endif
/*6.43*/
#ifdef NUM643
	cout <<"(a)內聯函數放在頭文件中. (b) 函數聲明放在頭文件中. "<<endl;
#endif
/*6.44*/
#ifdef NUM644
	cout << isShorter("c++", "linux") <<endl;
#endif
/*6.45*/
#ifdef NUM645
	cout << "內聯函數的聲明適合那些代碼短小,並且容易經常被調用的函數. "<<endl;
#endif
/*6.46*/
#ifdef NUM646
	cout << isShorter1("c++", "linux") <<endl;
#endif
/*6.47*/
#ifdef NUM647
	vector<int> vec{1,2,3,4,5};
	printVector(vec);
	cout <<endl;
#endif
/*6.48*/
#ifdef NUM648
	string s, sought("no");
	while(cin >>s && s != sought){ }
		assert(cin);
	cout<< "不合理,因為cin輸入總是有內容的,所以assert中的表達式總是為真,就不會執行assert. "<<endl;
#endif
/*6.49*/
#ifdef NUM649
	cout << "函數匹配的第一步是選定本次調用對應的重載函數集,集合中的函數稱為候選函數. 第二步考察本次調用提供的實參,然後從候選函數中選出能被這組實參調用的函數,這些新選出的函數稱為可行函數. "<<endl;
#endif
/*6.50*/
#ifdef NUM650
	cout << "(a) 2.56匹配double,但是42匹配int.(b)匹配void f(int). (c)匹配void f(int, int). (d)匹配void f(double, double = 3.14);"<<endl;
#endif
/*6.51*/
#ifdef NUM651
	f(2.56, 42);
	f(42);
	f(42, 0);
	f(2.56, 3.14);
	
#endif
/*6.52*/
#ifdef NUM652
	cout << "(a)3, 通過類型提升實現的匹配. (b)4, 通過算術類型轉換實現的匹配. "<<endl;
#endif
/*6.53*/
#ifdef NUM653
	cout <<"(a)沒有影響,const是頂層實現,第二句實現了函數重載. (b)非法,重復聲明."<<endl;
#endif
/*6.54*/
#ifdef NUM654
	cout <<"見main函數外聲明. "<<endl;
#endif
/*6.55*/
#ifdef NUM655
	vec.push_back(add);
	vec.push_back(substact);
	vec.push_back(multiply);
	vec.push_back(divide);
	for(vector<func6*>::iterator it = vec.begin(); it != vec.end(); ++it)
		cout << (*it)(100, 50)<< " ";  //*it兩端的括號必不可少, 否則函數返回vector類型的指針, 而非函數指針.
	cout <<endl;
#endif
	return 0;
}

參考資料:

c++ primer中文版第五版,電子工業出版社。

c++ primer第四版習題解答,人民郵電出版社。

Copyright © Linux教程網 All Rights Reserved