习题答案至于一个.cc 中,编译需要包含Chapter6.h头文件。 需要演示某一题直接修改 #define NUM***, 如运行6.23题为#define NUM623;
chapter 6
1、
形参初始化的机理与变量初始化一样。
当形参是引用类型时,它对应的实参被引用传递或者函数被传引用调用。
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中文版第五版,电子工业出版社。