C++ Primer第5版 第六章课后练习答案
练习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)<<" "; } }