筆記較為零散,都是自己不熟悉的知識點。
習題答案至於一個.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第四版習題解答,人民郵電出版社。