第 6 章 函数

练习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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值