練習6.1
形參指的是在函數的形參列表中聲明的局部變量。用實參初始化形參形參列表中的形參通常用逗號隔開,其中每個形參都是含有一個聲明符的聲明。
實參指的是函數調用時提供的值,用於初始化函數的形參。
練習6.2
(1)f()函數返回類型和f函數返回的值的類型不同,把f()函數改成string f()
(2)未定義f2()函數的返回類型,把f2()函數改成void f2(int i)
(3)形參名相同,把calc()函數改成int calc(int v1,int v2)
(4)函數體沒有加花括號限制范圍
練習6.3
template <typename T> T fact(T val) { T ret = val; while (val > 1) { ret *= --val; } return ret; }
練習6.4
template <typename T> T fact(T val) { if (val == 0 || val == 1)return 1; T ret = val; while (val > 1) { ret *= --val; } return ret; } int main() { int num1; cout << "Please input a number to calculate the factorial : "; cin >> num1; cout << "val's factorial is " << fact(num1) << endl; }
練習6.5
template <typename T> T absolute(T& val) { return val > 0 ? val : -val; } int main() { int num1; cin >> num1; cout << absolute(num1) << endl; }
練習6.6
形參:在函數的形參列表中聲明的局部變量。
局部變量:形參和函數體體內部定義的變量。
局部靜態變量:局部變量的生命周期貫穿函數調用及之后的時間。
template <typename T> T absolute(T& val) { T ret= val > 0 ? val : -val; static size_t count=0; cout << ++count<<" "; return ret; } int main() { int num1; cin >> num1; for(auto i=0;i<10;i++) cout << absolute(num1) << endl; }
練習6.7
size_t count() { static size_t count=0; return count++; } int main() { for(auto i=0;i<10;i++) cout << count() << endl; }
練習6.8
#ifndef CHAPTER6_H_ #define CHAPTER6_H_ template <typename T> T fact(T val); template <typename T> T absolute(T& val); #endif // !CHAPTER6_H_
練習6.9
fact.cpp
#include"Chapter6.h" #include <iostream> using namespace std; int fact(int val) { if (val == 0 || val == 1)return 1; int ret = val; while (val > 1) { ret *= --val; } return ret; } int absolute(int& val) { return val > 0 ? val : -val; }
factMain.cpp
#include <iostream> #include"Chapter6.h" using std::cin; using std::cout; using std::endl; int main() { int num; cin >> num; cout << fact(absolute(num)); }
cl /EHsc -c factMain.cpp cl /EHsc -c fact.cpp cl /EHsc factMain.obj fact.obj
練習6.10
void swap(int* a, int* b) { int c = *b; * b = *a; *a = c; } int main() { int num1,num2; cin >> num1>>num2; swap(&num1,&num2); cout << num1 << " " << num2; }
練習6.11
#include <iostream> using std::cin; using std::cout; using std::endl; template<typename T> void reset(T& a) { a = 0; } int main() { int num; cin >> num; reset(num); cout << num; }
練習6.12
void swap(int& a, int& b) { int c = b; b = a; a = c; } int main() { int num1,num2; cin >> num1>>num2; swap(num1,num2); cout << num1 << " " << num2; }
使用引用比使用指針更易於使用,因為代碼可讀性更高
練習6.13
void f(T) 實參被值傳遞,void f(&T)實參被引用傳遞,當初始化一個非引用類型的變量時,初始值被拷貝給變量。
練習6.14
前文中的swap函數中,參數就應該使用引用。find_char函數張參數c就不應該使用引用。
練習6.15
因為s字符串是不能被函數所修改的,所以是常量引用。c可能是一個臨時變量,所以不需要使用引用類型,也無需函數加以修改其本身。 如果令occurs為常量引用,則輸出occurs為0(不能加以修改),而s可能會在程序中被修改。
練習6.16
字符串s在函數中無需修改,所以最好是加上const 表示常量引用。
練習6.17
判斷是否含有大寫字母
bool check_string_upper(const string& s) { for (const auto& c : s) { if (isupper(c)) return true; } return false; }
將string對象改成小寫
void string_to_lower(string& s) { for (auto& c : s) { if (isupper(c)) tolower(c); } }
不相同。字符串s在在第一個函數中無需修改,所以最好是加上const 表示常量引用。但是第二個函數要對字符串進行修改
練習6.18
bool compare(const matrix&,const matrix&)
比較兩個matrix類對象是否相等
vector<int>::iterator change_val(int,vector<int>::iterator);
修改迭代器對應位置的值為i
練習6.19
(a):實參數量過多
(b):合法
(c):合法
(d):合法
練習6.20
引用形參不能被函數所修改時應該是常量引用。若此時將其設為普通引用那么不能將const對象、字面值或者需要類型轉換的對象傳遞給普通的引用形參,假如其它函數正確的將它們的形參定義為常量引用那么就無法在此類函數中正確使用。
練習6.21
int compare(int v1,int*v2) { return v1 > * v2 ? v1 : *v2; }
指針類型是int*
練習6.22
void swap(int* &v1,int* &v2) { int* temp = v2; v2 = v1; v1 = temp; }
練習6.23
template <typename T> void print(const T* beg, const T* end) { while (beg!=end) { cout << *beg++ << endl; } } template <typename T> void print(const T& val) { cout << val << endl; }
練習6.24
ia聲明為具有10個整數的數組,遍歷該數組並輸入存放的值。數組做參數時,會退化為指針,因此函數中的const int a[10]等同於const int *a,並且長度是不確定的,傳a[3]或a[255]是沒有區別的。因此如果我們要確定傳遞一個長度為10的數組,應該這樣定義:void print (const int (&a)[10]);
練習6.25
int main(int argc, char* argv[]) { string str; for (auto i = 0; i != argc; i++){ str += string(argv[i]); } cout << str << endl; }
練習6.26
int main(int argc, char* argv[]) { string str; for (auto i = 0; i != argc; i++){ str += string(argv[i]); } cout << str << endl; }
練習6.27
int list_total(initializer_list<int> list) { int sum = 0; for (auto beg = list.begin(); beg != list.end(); ++beg) { sum += *beg; } return sum; } int main(int argc, char* argv[]) { cout << list_total({ 0,10,21,34 }); }
練習6.28
const string&類型
練習6.29
當循環控制變量是基本類型時,可以不聲明為引用,否則應該聲明成引用,因為initializer_list對象可能是各種類型,有可能是自定義類型或者string類型。此時使用引用可以避免拷貝。
練習6.30
編譯器檢測出錯誤1 “str_subrange”: 函數必須返回值,沒有檢測出錯誤2
練習6.31
返回局部引用時無效,返回局部定義的常量引用無效。要確保返回的引用有效,就要確定引用所返回的是在函數之前已經存在的某個對象。
練習6.32
合法,函數的功能是返回對應整型指針偏移幾個地址中所存的值,這在本文中指遍歷具有十個整型的整型數組,並把對應下標當做值賦給數組
練習6.33
void print_vector(vector<int>::const_iterator it, vector<int>::const_iterator end) { if (it != end) { cout << *(it++)<<" "; print_vector(it, end); } } int main(int argc, char* argv[]) { vector<int> v{ 0,1,2,3,4 }; print_vector(v.begin(), v.end()); }
練習6.34
不發生變化
練習6.35
val -- 返回的是val的值,相當於又把val當作參數傳遞,遞歸將永遠不會停止,並且第一次遞歸不斷重復執行。
練習6.36
string(&func(string(&strs)[10]))[10];
練習6.37
類型別名
using strT = string[10]; strT(&func(strT&strs));
尾置返回類型
auto func(string(&strs)[10])->string(&)[10];
decltype關鍵字
decltype(str) (&func(decltype(str)(&strs)));
我覺得decltype 關鍵字更好,更直觀
練習6.38
decltype(odd) &arrPtr (int i) { return (i % 2) ? odd : even; }
練習6.39
(a)重復聲明了int cal(int,int),編譯器可以通過實參是否是常量來推測應該調用哪個函數
(b)錯誤,遇上一個函數相比只有返回類型不同
(c)新函數,參數類型不同屬於重載
練習6.40
(b)是錯誤的,因為默認形參右側的所有形參都必須有默認值
練習6.41
(a)非法
(c)合法,但是因為'*'是char型,會被隱式轉換成int,然后作為wd的值傳遞給函數
練習6.42
string make_plural(size_t ctr, const string& word, const string& ending = "s") { return (ctr > 1) ? word + ending : word; } int main(void) { string str1 = "success"; string str2 = "failure"; cout << make_plural(1, str1,"es") << endl; cout << make_plural(2, str1,"es") << endl; cout << make_plural(1, str2) << endl; cout << make_plural(2, str2) << endl; }
練習6.43
我會把(a)放在頭文件里,把(b)放在源文件中,因為inline在編譯階段內聯展開
練習6.44
inline bool isShorter(const string& s1, const string& s2) { return s1.size() < s2.size(); }
練習6.45
練習題中的函數短小的,沒有循環以及遞歸的應該被定義成內聯函數。改寫為內聯函數只需要在函數聲明前加inline關鍵字就可以。
練習6.46
函數“isShorter”不能生成常量表達式,因為isShorter函數中傳入的參數不是字面值類型,str1.size() < str2.size()返回的也不是字面值類型。
練習6.47
void print_vector(vector<int>::const_iterator it, vector<int>::const_iterator end) { #ifndef NDEBUG cout << "vector's size is " << end-it << endl; #endif // NDEBUG if (it != end) { cout << *(it++) << " "; print_vector(it, end); } } int main(int argc, char* argv[]) { vector<int> v{ 0,1,2,3,4 }; print_vector(v.begin(), v.end()); }
練習6.48
不合理,函數的意義是讓用戶進行輸入,直到輸入的字符串是sought是停止。因此assert (cin)一直為真,這條語句也就沒有意義。可以改為:assert ( s == sought)
練習6.49
候選函數:函數匹配的第一步是選定調用對應的重載函數集,集合中的函數稱為候選函數
可行函數:從第二步考察本次調用提供的實參,然后從候選函數中選出能被這組實參調用的函數
練習6.50
(a) f (2.56, 42) // 非法,因為實參類型是double, int,沒有可匹配的函數。如果不是重載函數,只有一個聲明f(double, double),則該語句合法。只有在重載時時非法的,要嚴格執行參數類型匹配。
(b) f (42) // 調用 f (int)
(c) f (42, 0) // 調用 f (int, int)
(d) f (2.56, 3.14) // 調用 f (double, double = 3.14)
練習6.51
練習6.52
(a)整型提升
(b)算數轉換
練習6.53
(a)合法,編譯器可以通過實參是否是常量來推測應該調用哪個函數
(b)合法,編譯器可以通過實參是否是常量來推測應該調用哪個函數
(c)非法,與第一行含義相同,屬於重復定義。
練習6.54
int func(int a, int b); typedef decltype(func)* func2; vector<func2> fvec;
練習6.55
int func(int a, int b); typedef decltype(func)* func2; vector<func2> fvec; inline int add(int a, int b) { return a + b; } inline int subtract(int a, int b) { return a - b; } inline int multiply(int a, int b) { return a * b; } inline int divide(int a, int b) { return static_cast<double>(a) / b; } int main(int argc, char* argv[]) { fvec.push_back(add); fvec.push_back(subtract); fvec.push_back(multiply); fvec.push_back(divide); }
練習6.56
int func(int a, int b); typedef decltype(func)* func2; vector<func2> fvec; inline int add(int a, int b) { return a + b; } inline int subtract(int a, int b) { return a - b; } inline int multiply(int a, int b) { return a * b; } inline int divide(int a, int b) { return static_cast<double>(a) / b; } int main(int argc, char* argv[]) { fvec.push_back(add); fvec.push_back(subtract); fvec.push_back(multiply); fvec.push_back(divide); int a = 10, b = 5; for (auto& i : fvec) { cout << i(a, b)<<" "; } }