练习6.1:
形参:在函数形参表中声明的局部变量。它们由每个函数调用中提供的参数初始化。
实数:函数调用中提供的值,用于初始化函数的形参。
练习6.2:
// (a)
string f() {
string s;
// ...
return s;
}
// (b)
void f2(int i) { /* ... */ }
// (c)
int calc(int v1, int v2) { /* ... */ }
// (d)
double square(double x) { return x * x; }
练习6.3:
#include <iostream>
using namespace std;
int fact(int val)
{
if (val == 0 || val == 1)
return 1;
else return val * fact(val - 1);
}
// 注意int的范围
int main()
{
cout << "Enter a number within [0, 13):" << endl;
for (int i = 0; cin >> i; cout << "Enter a number within [0, 13):" << endl)
{
if (i < 0 || i >12)
continue;
cout << i << "! = " << fact(i) << endl;
}
return 0;
}
习题6.4:
#include <iostream>
using namespace std;
int fact(int val)
{
if (val == 0 || val == 1)
return 1;
else return val * fact(val - 1);
}
void factorial_with_interacts()
{
cout << "Enter a number within [0, 13):" << endl;
for (int i = 0; cin >> i; cout << "Enter a number within [0, 13):" << endl)
{
if (i < 0 || i >12)
continue;
cout << i << "! = " << fact(i) << endl;
}
}
int main()
{
factorial_with_interacts();
return 0;
}
练习6.5:
template<typename T>
T abs(T val)
{
return val >= 0 ? val : -val;
}
练习6.6:
形参:在函数形参表中声明的局部变量。
局部变量:在块中定义的变量。
局部静态变量:将局部变量定义成static类型令其生命周期贯穿函数调用及之后的时间。局部静态变量(对象)在第一次执行经过对象定义语句时被初始化,并且直到程序终止时才被销毁。
练习6.7:
size_t func()
{
static size_t ctr = 0;
return ctr++;
}
练习6.8:
int fact(int val);
void factorial_with_interacts();
size_t func();
template<typename T>
T abs(T val)
{
return val >= 0 ? val : -val;
}
练习6.10:
#include<iostream>
using namespace std;
void swap(int *const lhs, int *const rhs)
{
int temp = *lhs;
*lhs = *rhs;
*rhs = temp;
}
int main()
{
cout << "enter two integer:" << endl;
for (int lhs, rhs; cin >> lhs >> rhs; cout << "enter two integer:" << endl)
{
swap(&lhs, &rhs);
cout << lhs << " " << rhs << endl;
}
return 0;
}
练习6.11:
#include<iostream>
using namespace std;
void set(int &i)
{
i = 0;
}
int main()
{
int i = 1;
set(i);
cout << i << endl;
return 0;
}
练习6.12:
#include<iostream>
using namespace std;
void swap(int &lhs, int &rhs)
{
int temp = lhs;
lhs = rhs;
rhs = temp;
}
int main()
{
cout << "enter two integer:" << endl;
for (int lhs, rhs; cin >> lhs >> rhs; cout << "enter two integer:" << endl)
{
swap(lhs, rhs);
cout << lhs << " " << rhs << endl;
}
return 0;
}
练习6.13:
void f(T)通过值传递参数。函数对形参的任何操作都不会影响实参。
void f(T&)通过引用传递参数,允许函数改变实参的值。
练习6.14:
// 形参是引用
void reset(int &i)
{
i = 0;
}
// 形参不能是引用
void print(vector<int>::iterator begin, vector<int>::iterator end)
{
for (std::vector<int>::iterator iter = begin; iter != end; ++iter)
cout << *iter << endl;
}
练习6.15:
为什么s是常量引用而occurs是普通引用?
s不应该被这个函数改变,所以是常量引用。但occurs的结果必须由函数来计算,所以是普通引用。
为什么s和occurs是引用类型而c不是?
c在这里可以使用const引用,但直接复制char会更节省内存。
如果令s是普通引用会发生什么?如果令occurs是常量引用呢?
s可以在函数中改变,occurs不会改变,所以occurs = 0是一个错误。
练习6.16:
bool is_empty(const string& s) { return s.empty(); }
因为这个函数不改变实参,所以应该加上"const"在string&s之前,否则该函数会产生误导,不能使用const string或const函数。
练习6.17:
#include<iostream>
#include<string>
using namespace std;
// 判断是否有大写字母
bool is_capital(const string &s)
{
for (auto c : s)
{
if (!isupper(c))
return true;
}
return false;
}
// 将大写字母转换为小写字母
const string &to_lower(string &s)
{
for (auto &c : s) // 必须引用 s才会修改
{
if (isupper(c))
c = tolower(c);
}
return s;
}
int main()
{
cout << "enter a string:" << endl;
for (string str; getline(cin, str); cout << "enter a string:" << endl)
{
cout << str << " has capital letter ? "
<< (is_capital(str) ? "true" : "false") << endl;
cout << "Convert all to lowercase letters:\n " << to_lower(str) << endl;
}
return 0;
}
练习6.18:
// (a)
bool compare(const matrix&, const matrix&);
// (b)
vector<int>::iterator change_val(int, vector<int>::iterator);
练习6.19:
(a) 非法,函数只有一个形参。 (b) 合法 (c) 合法 (d) 合法
练习6.20:
如果函数无需改变引用形参的值,最好将其声明为常量引用。把函数不会改变的形参定义成普通引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值。使用引用而非常量引用也会极大地限制函数所能接受的实参类型。例如,不能把const对象、字面值常量或者需要类型转换的对象传递给普通的引用形参。
练习6.21:
#include<iostream>
using namespace std;
int LargeOne(int ival, const int* const iptr)
{
return ival > *iptr ? ival : *iptr;
}
int main()
{
int i = 0;
cout << LargeOne(1, &i) << endl;
return 0;
}
练习6.22:
#include<iostream>
using namespace std;
void swap_ptr(const int*& ip1, const int*& ip2) // 必须引用,否则无法交换指针的值
{
auto temp = ip1;
ip1 = ip2;
ip2 = temp;
}
int main()
{
const int i = 42, j = 99;
auto ip1 = &i;
auto ip2 = &j;
// 指针的值和指针所指的值都改变了
cout << *ip1 << " " << *ip2 << endl;
cout << ip1 << " " << ip2 << endl;
swap_ptr(ip1, rhs);
cout << *ip1 << " " << *ip2 << endl;
cout << ip1 << " " << ip2 << endl;
return 0;
}
练习6.23:
#include<iostream>
using namespace std;
void print(const int* ip)
{
if (ip)
cout << *ip << endl;
}
void print(const char* cp)
{
if (cp)
{
while (*cp)
cout << *cp++;
}
cout << endl;
}
void print(const int* beg, const int* end)
{
while (beg != end)
cout << *beg++ << " ";
cout << endl;
}
// const int ia[]等价于const int* ia
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 elem : arr)
cout << elem << " ";
cout << endl;
}
int main()
{
int i = 0, j[2] = { 0,1 };
char ch[5] = "pezy";
print(&i);
print(ch);
print(begin(j), end(j));
print(j, end(j) - begin(j));
print(j);
return 0;
}
练习6.24:
// 形参传递指针
void print(const int ia[10])
{
for (size_t i = 0; i != 10; ++i)
cout << ia[i] << " ";
cout << endl;
}
// 代码能正确输出,等价于
void print(const int* ia)
{
for (size_t i = 0; i != 10; ++i)
cout << *(ia + i) << " ";
cout << endl;
}
// 改为形参传递数组
void print(const int (&ia)[10])
{
for (auto i : ia)
cout << i << " ";
cout << endl;
}
练习6.25 && 练习6.26:
#include<iostream>
#include<string>
using namespace std;
int main(int argc, char** argv)
{
string str;
// argv[0]保存程序的名字,而非用户输入
for (int i = 1; i != argc; ++i)
{
str += argv[i];
str += " ";
}
cout << str << endl;
}
练习6.27:
#include<iostream>
#include<initializer_list>
using namespace std;
int sum(const initializer_list<int>& il)
{
int sum = 0;
for (auto i : il)
sum += i;
return sum;
}
int main()
{
cout << sum({1, 2, 3, 4, 5}) << endl;
return 0;
}
练习6.28:
const string&
练习6.29:
取决于initializer_list元素的类型。当类型为PODType时,不需要引用。因为POD的复制成本很低(比如int)。否则,使用引用(const)是更好的选择。
练习6.30:
错误#1 “str_subrange”: 函数必须返回值
错误#2:控制流可能尚未返回任何值就结束了函数的执行,编译器可能检查不出这一错误。
练习6.31:
函数返回局部对象的引用时无效;当我们希望函数返回的对象可以被修改时,返回常量的引用无效。
练习6.32:
合法,给数组赋值。
练习6.33:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
template<typename T>
void printVec(T beg, T end)
{
if (beg != end)
{
cout << *beg << " ";
printVec(next(beg), end);
}
}
int main()
{
vector<string> vec{ "hello", "cap" };
printVec(vec.begin(), vec.end());
return 0;
}
练习6.34:
如果参数为正数,递归停止到零;如果参数为负数,递归永远不会停止。
练习6.35:
因为递归函数将始终使用val作为参数,会发生递归循环。
练习6.36:
string(&func(string (&arry)[10]))[10];
练习6.37:
// 使用类型别名
typedef string ArrT[10]; // using ArrT = string[10]
ArrT& func1(ArrT& arr);
// 使用尾置返回类型
auto func2(ArrT& arr)->string(&)[10];
// 使用decltype
string Arr[10];
decltype(Arr)& func3(decltype(Arr)& arr);
练习6.38:
int odd[] = { 1,3,5,7,9 };
int even[] = { 0,2,4,6,8 };
decltype(odd)& arrptr(int i)
{
return (i % 2) ? odd : even;
}
练习6.39:
(a) 重复声明,顶层const不影响传入函数的参数。
(b) 错误声明,不允许两个函数除了返回类型外其他所有的要素都相同。
(c) 合法。
练习6.40:
(a) 正确
(b) 错误,一旦某个形参被赋予了默认值,它后面的所有形参必须有默认值。
练习6.41:
(a) 非法,不匹配ht
(b) 合法,调用init(24,10,' ')。
(c) 合法,调用init(24, ' * ', ' '),char类型的实参隐式地转换成int,但不符初衷。
练习6.42:
#include<iostream>
#include<string>
using namespace std;
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;
return 1;
}
练习6.43:
两个都应该放在头文件中。(a)是内联函数。(b)是函数的声明。
练习6.44:
inline bool isShorter(const string& s1, const string& s2)
{
return s1.size() < s2.size();
}
练习6.45:
内联机制用于优化规模小,流程直接,频繁调用的函数。
练习6.46:
constexpr函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。string 类型不是字面值类型。
练习6.47:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
//#define NDEBUG
template<typename T>
void printVec(T 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<string> vec{ "Hello", "Cpp" , "Java"};
printVec(vec);
cout << endl;
return 0;
}
练习6.48:
这个循环允许用户一直输入一个单词直到找到该单词为止。
这不是assert的一个好用法,assert宏常用于检查“不能发生”的条件。但是assert总是在用户输入eof时发生,这种行为很自然,检查毫无必要。使用 assert (!cin || s == sought)会更好。
练习6.49:
函数匹配中第一步中,选定调用对应的重载函数集,集合中的函数为候选函数。候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。
第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,称为可行函数。候选函数也有两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或者可以转换成形参的格式。
练习6.50:
(a) 不合法,调用具有二义性。
(b) 匹配 void f(int)
(c) 匹配 void f(int, int)
(d) 匹配 void f(double, double = 3.14)
练习6.51:
#include<iostream>
using namespace std;
void f() { cout << "f()" << endl; }
void f(int i) { cout << "f(int)" << endl; }
void f(int i1, int i2) { cout << "f(int, int)" << endl; }
void f(double d1, double d2 = 3.14) { cout << "f(double, double = 3.14)" << endl; }
int main()
{
//f(2.56, 42);
f(42);
f(42, 0);
f(2.56, 3.14);
return 0;
}
练习6.52:
(a) 通过类型提升实现的匹配
(b) 通过算数类型转换实现的匹配
练习6.53:
(a) 第一句只能调用非常量对象,第二句都可以.
(b) 第一句的实参是指向非常量的指针,第二句都可以。
(c) 不合法。顶层const,都是调用指向字符类型的指针
练习6.54:
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);
vector<pFunc1> vec1;
vector<pFunc2> vec2;
vector<pFunc3> vec3;
vector<pFunc4*> vec4;
vector<pFunc5> vec5;
vector<pFunc6*> vec6;
练习6.55 && 练习6.56:
#include<iostream>
#include<vector>
using namespace std;
int func(int a, int b);
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; }
int main()
{
vector<decltype(func)*> vec{ add, subtract, multiply, divide };
for (auto f : vec)
cout << f(2, 2) << endl; // (*f)(2, 2)
return 0;
}