第六章 函数
6.1
形参:在函数参数列表中声明的局部变量,它们由每个函数调用中提供的参数初始化。
实参:函数调用中提供的值,用于初始化函数的参数。
6.2
(a) 返回值类型应该是string
(b) 返回值类型为void
(c) 函数体缺少左花括号
(d) 函数体没有花括号
6.3
#include <iostream>
int fact(int val)
{
if (val == 0 || val == 1) return 1;
else return val * fact(val-1);
}
int main()
{
int j = fact(5); // j equals 120, i.e., the result of fact(5)
std::cout << "5! is " << j << std::endl;
return 0;
}
6.4
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
int fact(int val)
{
int ret = 1;
while (val > 1) ret *= val--;
return ret;
}
void factorial_with_interacts()
{
for (int val = 0; cout << "Enter a number within [0, 13): ", cin >> val;) {
if (val < 0 || val > 12) continue;
cout << val << "! =" << fact(val) << endl;
}
}
int main()
{
factorial_with_interacts();
}
6.5
template <typename T>
T abs(T i)
{
return i >= 0 ? i : -i;
}
6.6
局部变量:在块中定义的变量;
形参:在函数参数列表中声明的局部变量
局部静态变量:在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在函数结束执行也不会对它有影响。
// example
size_t count_add(int n) // n is a parameter.
{
static size_t ctr = 0; // ctr is a static variable.
ctr += n;
return ctr;
}
int main()
{
for (size_t i = 0; i != 10; ++i) // i is a local variable.
cout << count_add(i) << endl;
return 0;
}
6.7
size_t generate()
{
static size_t ctr = 0;
return ctr++;
}
6.8
Chapter6.h
int fact(int val);
int func();
template <typename T> T abs(T i)
{
return i >= 0 ? i : -i;
}
6.9
fact.cc
#include "Chapter6.h"
#include <iostream>
int fact(int val)
{
if (val == 0 || val == 1)
return 1;
else
return val * fact(val - 1);
}
int func()
{
int n, ret = 1;
std::cout << "input a number: ";
std::cin >> n;
while (n > 1) ret *= n--;
return ret;
}
factMain.cc
#include "Chapter6.h"
#include <iostream>
int main()
{
std::cout << "5! is " << fact(5) << std::endl;
std::cout << func() << std::endl;
std::cout << abs(-9.78) << std::endl;
}
6.10
#include <iostream>
#include <stdexcept>
#include <string>
using std::cin;
using std::cout;
using std::endl;
void swap(int* const lhs, int* const rhs)
{
auto tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
int main()
{
for (int lht, rht; cout << "Please Enter:\n", cin >> lht >> rht;) {
swap(&lht, &rht);
cout << lht << " " << rht << endl;
}
}
6.11
#include <iostream>
void reset(int& i)
{
i = 0;
}
int main()
{
int i = 42;
reset(i);
std::cout << i << std::endl;
}
6.12
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
void swap(int& lhs, int& rhs)
{
auto tmp = lhs;
lhs = rhs;
rhs = tmp;
}
int main()
{
for (int left, right; cout << "Please Enter:\n", cin >> left >> right;) {
swap(left, right);
cout << left << " " << right << endl;
}
}
6.13
void f(T)被传值调用,函数对形参做的所有操作都不会影响实参:void f(&T)被传引用调用,引用形参绑定传入的实参,可以改变其值。
6.14
一个形参应该是引用类型:
void reset(int &i)
{
i = 0;
}
一个形参不能是引用类型:
void print(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
for (std::vector<int>::iterator iter = begin; iter != end; ++iter)
std::cout << *iter << std::endl;
}
6.15
(a) s在函数中不会被修改,但occur的结果必须通过函数去计算
(b) 避免直接拷贝string对象,直接拷贝char会更加高效。
(c) s可能会被修改,occurs不会被改变。
6.16
因为这个函数不会改变传入的参数,形参应该被定义成const string& s,否则会给调用者误导,即可以修改它的值,同时也限制了函数所能接受的实参类型,不能把const string,字面值,需要类型转化的对象传给形参,而且不能在调用该常量引用的函数中使用。
6.17
#include <iostream>
#include <string>
using std::string;
bool hasUppercase(const string& str)
{
for (auto c : str)
if (isupper(c)) return true;
return false;
}
const string& makeLowercase(string& str)
{
for (auto& c : str)
if (isupper(c)) c = tolower(c);
return str;
}
int main()
{
string str("Hello World!");
std::cout << std::boolalpha << hasUppercase(str) << std::endl;
std::cout << makeLowercase(str) << std::endl;
}
6.18
(a) bool compare (const matrix& m1, const matreix& m2);
(b) vector<int>::iterator change_val (int, vector<int>::iterator);
6.19
(a)只用一个参数
(b,c,d)合法
6.20
能用就用,当引用的值需要改变设为普通引用。如果我们让一个参数成为一个普通的引用,而它应该是一个常量的引用,那么这个引用值可能会改变。
6.21
#include <iostream>
int LargerOne(int i, const int* const ip)
{
return (i > *ip) ? i : *ip;
}
int main()
{
int c = 6;
std::cout << LargerOne(7, &c) << std::endl;
}
6.22
#include <iostream>
void swap(const int*& lhs, const int*& rhs)
{
auto temp = lhs;
lhs = rhs;
rhs = temp;
}
int main()
{
const int i = 42, j = 99;
auto lhs = &i;
auto rhs = &j;
swap(lhs, rhs);
std::cout << *lhs << " " << *rhs << std::endl;
}
6.23
#include <iostream>
using std::begin;
using std::cout;
using std::end;
using std::endl;
void print(int* const pi)
{
if (pi) cout << *pi << endl;
}
void print(const char* p)
{
if (p)
while (*p) cout << *p++;
cout << endl;
}
void print(const int* beg, const int* end)
{
while (beg != end) cout << *beg++ << " ";
cout << endl;
}
void print(const int ia[], size_t size)
{
for (size_t i = 0; i != size; ++i) cout << ia[i] << " ";
cout << endl;
}
void print(const int (&arr)[2])
{
for (auto i : arr) cout << i << " ";
cout << endl;
}
int main()
{
int i = 0, j[2] = {0, 1};
char ch[5] = "pezy";
print(ch);
print(begin(j), end(j));
print(&i);
print(j, end(j) - begin(j));
print(const_cast<const int(&)[2]>(j));
}
6.24
因为不能拷贝数组,所有我们无法以值传递的方式使用数组参数。因为数组会被转换为指针,所以我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
在这个问题中,const int ia[10] 实际上与const int*一样,数组的大小是不相关的,这里可以传const int ia[3]也可以是const int ia[255].如果想要传递一个大小为10的数组,我们应该使用引用像这样:void print10(const int (&ia)[10]) {/*...*/}
6.25 & 6.26
#include <iostream>
#include <string>
int main(int argc, char** argv)
{
std::string str;
for (int i = 1; i != argc; ++i) {
str += argv[i];
str += " ";
}
std::cout << str << std::endl;
}
6.27
#include <initializer_list>
#include <iostream>
int sum(const std::initializer_list<int>& il)
{
int sum = 0;
for (auto i : il) sum += i;
return sum;
}
int main(void)
{
std::cout << sum({1, 2, 3, 4, 5}) << std::endl;
}
6.28
const string& 常量字符串引用
6.29
取决于initializer_list元素的类型。当类型为PODType时,不需要引用。因为POD的复制成本很低(比如int)。否则,使用引用(const)是更好的选择。
6.30
错误#1:没有返回一个值
错误#2:控制流可能尚未返回任何值就结束了函数的执行,编译器可能检查不出这一错误。
6.31
(1) 当返回局部对象的引用时无效
(2) 当我们希望返回的对象被修改时,被返回的常量引用无效。
when you can find the preexisted object that the reference refered.(这个答案感觉不符合)
6.32
合法,给数组赋值了0~9。
6.33
#include <iostream>
#include <vector>
using std::cout;
using std::vector;
using Iter = vector<int>::iterator;
void print(Iter beg, Iter end)
{
if (beg != end) {
cout << *beg << " ";
print(std::next(beg), end);
}
}
int main()
{
vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9};
print(vec.begin(), vec.end());
}
6.34
如果参数为正数,递归停止到零;如果参数为负数,递归不会停止。
6.35
递归函数将始终使用val作为参数,会发生递归循环。
6.36
string (&func(int i)) [10]
string (&func(string (&arrStr) [10])) [10]
6.37
using ArrT = string [10];
ArrT& func1(ArrT& arr);
auto func2(ArrT& arr) -> string(&)[10];
string arrS[10];
decltype(arrS)& func3(ArrT& arr);
我喜欢第一种方式,因为理解以后使用简洁方便
6.38
decltype(odd)& arrPtr(int i)
{
return (i % 2) ? odd : even;
}
6.39
(a) 重复声明。
(b) 错误声明,不允许两个函数除了返回类型外其他所有的要素都相同。
(c) 合法。
6.40
(a) 正确
(b) 错误,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须有默认值。
6.41
(a) 非法,不匹配ht
(b) 合法。
(c) 合法,不符初衷,wd会被赋值为机器上该符号对应的数值。
6.42
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
string make_plural(size_t ctr, const string& word, const string& ending = "s")
{
return (ctr > 1) ? word + ending : word;
}
int main()
{
cout << "singual: " << make_plural(1, "success", "es") << " "
<< make_plural(1, "failure") << endl;
cout << "plural : " << make_plural(2, "success", "es") << " "
<< make_plural(2, "failure") << endl;
}
6.43
都放,(a)是内联函数,(b)是函数声明
6.44
#include <iostream>
#include <string>
using std::string;
inline bool isShorter(const string& s1, const string& s2)
{
return s1.size() < s2.size();
}
int main()
{
std::cout << isShorter("pezy", "mooophy") << std::endl;
}
6.45
内联机制用于优化规模小,流程直接,频繁调用的函数。
6.46
constexpr函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。string 类型不是字面值类型。
6.47
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
//#define NDEBUG
void printVec(vector<int>& vec)
{
#ifndef NDEBUG
cout << "vector size: " << vec.size() << endl;
#endif
if (!vec.empty()) {
auto tmp = vec.back();
vec.pop_back();
printVec(vec);
cout << tmp << " ";
}
}
int main()
{
vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9};
printVec(vec);
cout << endl;
}
6.48
这个循环允许用户一直输入一个单词直到找到该单词为止。
这不是assert的一个好用法,assert宏常用于检查“不能发生”的条件。但是assert总是在用户输入eof时发生,这种行为很自然,检查毫无必要。使用 assert (!cin || s == sought)会更好。
6.49
函数匹配中第一步中,选定调用对应的重载函数集,集合中的函数为候选函数.
2个特征:1.与被调用的函数同名,2,其声明在调用点可见。
第二部考察本次调用提供的实参,从候选函数中选出能被这组实参调用的函数,称为可行函数.
2个特征:1.其形参数量与本次调用提供的实参数量相等,2.每个实参的类型与对应的形参类型相同,或者可以转换成形参的格式。
6.50
(a) 不合法,调用具有二义性。
(b)匹配于 void f(int)
(c)匹配于 void f(int, int)
(d)匹配于 void f(double, double = 3.14)
6.51
#include <iostream>
using std::cout;
using std::endl;
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)
{
cout << "f(double, double)" << endl;
}
int main()
{
// f(2.56, 42); // error: 'f' is ambiguous.
f(42);
f(42, 0);
f(2.56, 3.14);
}
6.52
(a) 通过类型提升实现的匹配3
(b) 通过算数类型转换4
6.53
(a) 第一句只能调用非常量对象,第二句都可以.
(b) 第一句的实参是指向非常量的指针,第二句都可以。
(c) 不合法。都是调用指向字符类型的指针
6.54
int func(int a, int b);
using PF = int (*)(int a, int b);
vector<PF> vec;
int func(int a, int b);
using pFunc1 = decltype(func) *;
typedef decltype(func) *pFunc2;
using pFunc3 = int (*)(int a, int b);
using pFunc4 = int(int a, int b);
typedef int(*pFunc5)(int a, int b);
using pFunc6 = decltype(func);
std::vector<pFunc1> vec1;
std::vector<pFunc2> vec2;
std::vector<pFunc3> vec3;
std::vector<pFunc4*> vec4;
std::vector<pFunc5> vec5;
std::vector<pFunc6*> vec6;
6.55
int add(int a, int b) {return a + b;}
int subtract(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;}
6.57
vector<decltype(func)*> vec{add, subtract, multiply, divide};
for (auto f: vec)
cout << f(2, 2) << endl;
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::vector;
//!
//! @brief Exercise 6.54
//! @note define the function type fp
//!
inline int f(const int, const int);
typedef decltype(f) fp; // fp is just a function type not a function pointer
//!
//! @brief Exercise 6.55
//! @note Store pointers to these functions in the vector
//!
inline int NumAdd(const int n1, const int n2)
{
return n1 + n2;
}
inline int NumSub(const int n1, const int n2)
{
return n1 - n2;
}
inline int NumMul(const int n1, const int n2)
{
return n1 * n2;
}
inline int NumDiv(const int n1, const int n2)
{
return n1 / n2;
}
vector<fp*> v{NumAdd, NumSub, NumMul, NumDiv};
int main()
{
//!
//! @brief Exercise 6.56
//! @note Call each element in the vector and print their result.
//!
for (auto it = v.cbegin(); it != v.cend(); ++it)
cout << (*it)(2, 2) << std::endl;
return 0;
}