c++ primer(第五版)学习笔记及习题答案代码版(第六章)函数

笔记较为零散,都是自己不熟悉的知识点。

习题答案至于一个.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中文版第五版,电子工业出版社。

c++ primer第四版习题解答,人民邮电出版社。

时间: 2024-11-03 20:59:45

c++ primer(第五版)学习笔记及习题答案代码版(第六章)函数的相关文章

c++ primer(第五版)学习笔记及习题答案代码版(第十四章)重载运算与类型转换

笔记较为零散,都是自己不熟悉的知识点. 习题答案至于一个.h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行14.30题为#define NUM1430: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful f

c++ primer(第五版)学习笔记及习题答案代码版(第十一章)关联容器

笔记较为零散,都是自己不熟悉的知识点. 习题答案至于一个.cc 中,包含Chapter7.h头文件,读入文件包括./test ./rules .需要演示某一题直接修改 #define NUM****, 如运行11.23题为#define NUM1123: chapter 11 1.  关联容器不支持顺序容器的位置相关的操作,例如push_front或push_back.原因是关联容器中元素是根据关键字存储的,这些操作对 关联容器没有意义.而且关联容器也不支持构造函数或插入操作这些接收一个元素值和

快学scala 第十一章 操作符 读书笔记及习题答案代码

chapter 11 操作符 标签:快学scala 一.笔记 scala种可以在反引号中包含几乎任何字符序列, val 'val' = 42 所有的操作符都是左结合的,除了以冒号(:)结尾的操作符,和赋值操作符.用于构造列表的::操作符是又结合的.1::2::Ni1的意思是1::(2::Ni1),先创建出包含2的列表,这个列表又被作为尾巴拼接到以1作为头部的列表中. 2. 函数调用语法:f(arg1, arg2,...)扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等于f.a

C++ Primer 第五版学习笔记

<C++ Primer>第五版中文版学习笔记 ? C++ Primer 第五版学习笔记

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器 多重映照容器multimap与map结构基本相同,但由于重复键值存在,所以multimap的元素插入.删除.查找都与map的方法不相同. 1.multimap对象创建.元素插入 插入元素时,需要使用insert()方法和类似pair<string,double>("Jack", 300.5)的元素结构.可以看到,重复的元素是按照插入的先后顺序排序的. #include <iostre

C++ Primer(第五版)学习笔记_5_标准模板库string(2)

C++ Primer(第五版)学习笔记_5_标准模板库string(2) 10.搜索string对象的元素或子串 采用find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定):如果查到,则返回下标值(从0开始计数),如果查不到,则返回一个很大的数string:npos(即:4294967295). #include <iostream> #include <stdio.h> #include <string> using nam

C++ Primer(第五版)学习笔记_3_标准模板库vector(2)

C++ Primer(第五版)学习笔记_3_标准模板库vector(2) 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 6.元素的插入 insert()方法可以在vector对象的任意位置前插入一个新的元素,同时,vector自动扩张一个元素空间,插入位置后的所有元素依次向后挪动一个位置. 要注意的是,insert()方法要求插入的位置,是元素的迭代器位置,而不是元素的下标. #include <iostream> #include <vector> using namespa

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 标准模板库(STL)提供三种类型的组件:容器.迭代器和算法,他们都支持泛型程序设计标准. 容器主要有两类:顺序容器和关联容器.顺序容器(vector.list.deque和string等)是一系列元素的有序集合.关联容器(set.multiset.map和multimap)包含查找元素的键值. 迭代器的作用是遍历容器. STL算法库包含四类算法:排序算法.不可变序算法.变序性算法

C++ Primer(第五版)学习笔记_2_标准模板库vector(1)

C++ Primer(第五版)学习笔记_2_标准模板库vector(1) 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 向量容器vector不但能像数组一样进行随机访问,还能在尾部插入元素,完全可以替代数组. 值得注意的是,vector具有内存自动管理的功能,对于元素的插入和删除,可以动态调整所占的内存空间. 容器vector的下标是从0开始的,如果vector容器的大小是n,则元素下标为0~n-1,这和数组的一样的.不一样的是,vector可以随时调整其大小. vector重要的方法有三个