《C++primer(第五版)》学习之路-第六章:函数

本文深入探讨了函数的基础概念,包括定义、调用、参数传递方式等,并详细讲解了函数重载、递归、内联函数等高级特性,以及函数指针的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【 声明:版权所有,转载请标明出处,请勿用于商业用途。  联系信箱:libin493073668@sina.com】


6.1 函数基础


1.函数是一个命名了的代码块,我们通过调用函数执行相应的代码。函数可以有0个或者多个参数,而且(通常)会产生一个结果。可以重载,也就是说,同一个名字可以对应几个不同的名字。


2.一个典型的函数定义包括以下部分:返回类型,函数名字,由0个或多个形参组成的列表以及函数体。其中,形参以逗号隔开,形参的列表位于一对圆括号之内。函数执行的操作在语句块中说明,该语句块成为函数体。


3.我们通过调用运算符来执行函数。调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针:圆括号之内是一个用括号给的实参列表,我们用实参初始化函数的形参。调用表达式的类型就是函数的返回类型。


4.名字的作用域是程序文本的一部分,名字在其中可见。

对象的生命周期是程序执行过程中该对象存在的一段时间。

形参和函数体内部定义的变量统称为局部变量。


5.我们把只存在于块执行期间的对象称为自动对象。当块执行结束后,块中创建的自动对象的值就变成未定义的了。


6.局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。


6.2 参数传递


1.当形参是引用类型时,我们说它对应的实参被引用传递或者函数被传引用调用。

当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象。我们说这样的实参被值传递或者函数被传值调用。


2.如果函数无需改变引用形参的值,最好将其声明为常量引用。


3.initializer_list:
使用时包含头文件:
#include<initializer_list>

initializer_list提供的操作:
initializer_list<T>1st:              默认初始化;T类型元素的空列表
initializer_list<T>1st{a,b,c...}: 1st的元素数量和初始值一样多;1st的元素是对应初始值的副本;列表中的元素是const
1st2(1st):                                拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素
1st2 = 1st:                              同上
1st.size():                               列表中的元素数量
1st.begin():                             返回指向1st中首元素的指针
1st.end():                               返回指向1st中尾元素下一位置的指针


6.3 返回类型和return语句


1.没有返回值的return语句只能用在返回类型时void的函数中。

只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式的转换成函数的返回类型。


2.如果一个函数调用了它自身,不管这种调用时直接的还是间接的,都称该函数为递归函数。


6.4 函数重载


1.如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。

不允许两个函数除了返回类型外其他所有的要素都相同。


2.函数匹配是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某一个关联起来,函数匹配也叫做重载确定。


3.调用重载函数时有三种可能的结果:

编译器找到一个与实参最佳匹配的函数,并生成调用该函数的代码。

找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息。

有多于一个函数可以匹配,但是每一个都不是最佳选择。此时也将发生错误,称为二义性调用。


6.5 特殊用途语言特性


1.某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函数时,可以包含该实参,可以省略该实参。

一旦某个形参被赋予了默认值,它后面所有形参都必须有默认值。


2.内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。

一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数,很多编译器都不支持内联递归函数。


3.constexpr函数是指能用于常量表达式的函数。

函数返回类型及所有形参的类型都得是字面值类型,而且函数体重必须有且只有一条return语句。


4.

_ _func_ _    输出当前调试的函数的名字 

_ _FILE_ _   存放文件名的字符串字面值

_ _LINE_ _   存放当前行号的整型字面值

_ _TIME_ _  存放文件编译时间的字符串字面值

_ _DATE_ _  存放文件编译日期的字符串字面值


6.6 函数匹配


1.函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。

第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。可行函数也有两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。


2.为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个风机,具体排序如下所示:

a.精确匹配,包括以下情况:

实参类型和形参类型相同

实参从数组类型或函数类型转换成对应的指针类型

向实参添加顶层const或者从实参中删除底层const

b.通过const转换实现的匹配

c.通过类型提升实现匹配

d.通过算术类型转换

e.通过类类型转换实现匹配


6.7 函数指针


1.函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和形参类型共同决定,与函数名无关。


2.当我们使用重载函数时,上下文必须清晰地界定到底应该选用哪个函数。如果定义了指向重载函数的指针,编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数中的某一个精确匹配。


PS:部分练习答案


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

int fact(int val)
{
	if (val == 0 || val == 1) return 1;
	else return val * fact(val-1);
}

int main()
{
	int j = fact(5);
	std::cout << "5! is " << j << std::endl;
	return 0;
}

练习6.4

#include <iostream>

void factorial()
{
	int num;
	std::cout << "Please input a positive number: ";
	while (std::cin >> num && num < 0)
		std::cout << "Please input a positive number again: ";
	std::cout << num;

	unsigned long long result = 1;
	while (num > 1) result *= num--;

	std::cout << "! is ";
	if (result)
		std::cout << result << std::endl;
	else
		std::cout << "too big" << std::endl;
}

int main()
{
	factorial();
}


练习6.5

template <typename T>
T abs(T i)
{
    return i >= 0 ? i : -i;
}

练习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 <string>
#include <stdexcept>

void swap(int* a, int* b)
{
	int tmp;
	tmp = *a;
	*a= *b;
	*b= tmp;
}

int main()
{
	int a,b;
	std::cout << "Please Enter:\n";
	while(std::cin >> a >> b)
	{
		swap(&a, &b);
		std::cout << a << " " << b << std::endl;
		std::cout << "Please Enter:\n";
	}

	return 0;
}

练习6.11

#include <iostream>

void reset(int& i)
{
	i = 0;
}

int main()
{
	int i = 42;
	reset(i);
	std::cout << i << std::endl;
	return 0;
}

练习6.12

#include <iostream>
#include <string>

void swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

int main()
{
	int a,b;
	std::cout << "Please Enter:\n";
	while(std::cin >> a >> b)
	{
		swap(a, b);
		std::cout << a << " " << b << std::endl;
		std::cout << "Please Enter:\n";
	}

	return 0;
}

练习6.17

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

bool hasUppercase(const string& str)
{
	for (auto c : str)
		if (isupper(c)) return true;
	return false;
}

void makeLowercase(string& str)
{
	for (auto& c : str)
		if (isupper(c)) c = tolower(c);
}

int main()
{
	string str("Hello World!");
	cout << hasUppercase(str) << endl;
	makeLowercase(str);
	cout << str << endl;

	return 0;
}

练习6.18

a

bool compare(matrix &m1, matrix &m2){ /.../ }
b

vector<int>::iterator change_val(int, vector<int>::iterator) { /.../ }

练习6.21

#include <iostream>
using std::cout;

int Max(const int i, const int* ip)
{
	return (i > *ip) ? i : *ip;
}

int main()
{
	int c = 6;
	cout << Max(7, &c);

	return 0;
}


练习6.22

#include <iostream>

void swap(int*& l, int*& r)
{
    auto tmp = l;
    l = r;
    r = tmp;
}

int main()
{
    int i = 42, j = 99;
    auto l = &i;
    auto r = &j;
    swap(l, r);
    std::cout << *l << " " << *r << std::endl;

    return 0;
}

练习6.23

#include <iostream>

using std::cout;
using std::endl;
using std::begin;
using std::end;

void print(const int* 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++ << endl;
}

void print(const int ia[], size_t size)
{
	for (size_t i = 0; i != size; ++i)
	{
		cout << ia[i] << endl;
	}
}

void print(int(&arr)[2])
{
	for (auto i : arr) cout << i << 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(j);

	return 0;
}

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

练习6.27

#include <iostream>
#include <initializer_list>

int sum(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;
    return 0;
}

练习6.33

#include <iostream>
#include <vector>

using std::vector;
using std::cout;
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());
	return 0;
}

练习6.36

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(arrStr)& arrPtr(int i)
{
	return (i % 2) ? odd : even;
}

练习6.42

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

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 0;
}

练习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.47

#include <iostream>
#include <vector>
using std::vector;
using std::cout;
using std::endl;

#define NDEBUG

void printVec(vector<int>& vec)
{
#ifdef 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;

	return 0;
}

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

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.56

std::vector<decltype(func) *> vec{add, subtract, multiply, divide};
for (auto f : vec)
          std::cout << f(2, 2) << std::endl;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值