C++Primer第五版 第六章 课后习题答案
第六章
6.6
形参、局部变量和局部静态变量
- 形参和定义在函数体内部的变量统称为局部变量,它们仅在函数的作用域内可见
- 函数体内的局部变量可以分为普通局部变量和静态局部变量
- 形参:在函数开始时便为其申请内存空间,调用时用实参进行初始化。形参是只存在于块执行期间的对象
- 普通局部变量在所载块结束后就失效了
- 静态局部变量对应的对象称为局部静态对象,它的生命周期从定义语句开始一直到程序结束
6.8
在头文件内进行函数声明:和变量类似,在头文件声明然后在源文件定义。预处理器变量的名字全部大写,可以避免与程序中别的实体发生名字冲突。
#ifndef CHAPTER_H_INCLUDED
#define CHAPTER_H_INCLUDEDint fact(int);
double myABS(double);#endif
以下引用自:#pragma指令与#ifndef指令区别
在C/C++中,在使用预编译指令#include的时候,为了防止重复引用造成二义性,通常有两种方式——
- 第一种是#ifndef指令防止代码块重复引用,比如说
#ifndef _CODE_BLOCK #define _CODE_BLOCK // code #endif// _CODE_BLOCK` ```
- 第二种就是#pragma once指令,在想要保护的文件开头写入
#pragma once
- #ifndef方式是C/C++语言的标准支持,也是比较常用的方式,#ifndef的方式依赖于自定义的宏名(例中的_CODE_BLOCK)不能冲突,它不光可以保证同一份文件不会被包含两次,也能够保证不同文件完全相同的内容不会被包含两次。
但,同样的,如果自定义的宏名不小心“重名”了,两份不同的文件使用同一个宏名进行#ifndef,那么会导致编译器找不到声明的情况(被编译器判定为重定义而屏蔽了)
此外,由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,#ifndef会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式(Visual Studio 2017新建头文件会自带#pragma once指令)- #pragma once一般由编译器提供保证:同一个文件不会被包含多次。这里所说的”同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。无法对一个头文件中的一段代码作#pragma once声明,而只能针对文件。
此方式不会出现宏名碰撞引发的奇怪问题,大型项目的编译速度也因此提供了一些。缺点是如果某个头文件有多份拷贝,此方法不能保证它们不被重复包含。在C/C++中,#pragma once是一个非标准但是被广泛支持的方式。
- 二者的区别:
#pragma once方式产生于#ifndef之后。#ifndef方式受C/C++语言标准的支持,不受编译器的任何限制;而#pragma once方式有些编译器不支持(较老编译器不支持,如GCC 3.4版本之前不支持#pragmaonce),兼容性不够好。#ifndef可以针对一个文件中的部分代码,而#pragma once只能针对整个文件
6.9(分离式编译)
- 分离式编译允许我们把程序分割到几个文件中,每个文件独立编译
- 举例:(C++Primer第五版 第6.1.3节)
fact函数定义在fact.cc中;fact函数声明位于Chapter.h中;fact函数在factMain.cc文件中的main函数中被调用。
这一过程通常会产生一个后缀名为.obj(Windows)或者.o(UNIX)的文件,含义是该文件包含object code。
编译器会把对象文件链接在一起形成可执行文件。 - 分离式编译时具体代码
fact.cc
#include "Chapter6.h"
using namespace std;
//定义函数fact
int fact(int val)
{if (val < 0)return -1;int ret = 1;for (inti = 1; i != val + 1; ++i)ret *= i;return ret;
}
factMain.cc
#include "Chapter6.h"
#include <iostream>
using namespace std;
//调用函数fact
int main()
{int num;cout << "请输入一个数" << endl;cin >> num;cout << num << "的阶乘是:" << fact(num) << endl;return 0;
}
6.12(传值和传引用)
使用引用可以直接操作所引用的对象,而且可以避免拷贝。
void f(T): 形参采用传值的方式,实参被拷贝给形参
void f(T&):形参采用传引用的方式,实参是形参的别名
6.14(引用传递相对于值传递的优势)![](/assets/blank.gif)
6.23
三个版本print函数:依次调用,输出int i = 0;
和 int j[2] = {0, 1};
的 i 和 j
- 不控制指针的边界(参数是常量整型指针)
void print1(const int *p)
{cout << *p << endl;
}
- 调用者指定数组的维度(两个参数,分别是常量整型指针和数组的容量)
void print2(const int *p, const int sz)
{int i = 0;while(i != sz){cout << *p++ << endl;++i;}
}
- 使用C++11新规定的begin和end函数限定数组边界(两个参数,分别是数组的首尾边界)
void print3(const int *b, const int *e)
{for (auto q = b; q != e; ++q)//记得用后置运算符{cout << *q << endl;}
}
- 调用如下
print1(&i1);print1(j);print2(&i, 1);print2(j, sizeof(j)/sizeof(*j));print3(begin(j), end(j));
6.24(不指定形参数组维度)
void print(const int ia[10]) { //循环输出数组的值}
形参ia的维度10此时只是我们期望的,但是实际上不一定,而且真实维度如果不是10我们依然可以正常调用print函数
修改后应该是:void print(const int ia[], const int sz) { //循环输出数组的值}
6.25(main处理命令行选项 6.2.5节)
带参数的main与控制台命令行输入
6.27(含有可变形参的函数 6.2.6节)
一个可以计算列表中所有元素的和的函数:
#include <iostream>
using namespace std;int iCount(initializer_list<int> il)
{int count = 0;for (auto i : il){count += i;}return count;
}int main()
{//列表初始化initializer_list<T>cout << "1,1,2,2,3的和是:" << iCount({ 1,1,2,2,3 }) << endl;return 0;
}
6.31(引用何时有效)
引用所引的是函数开始之前就已经存在的对象,则返回该引用是有效的
引用的是函数的局部变量则随着函数的结束,局部变量失效,此时返回的引用无效
不希望改变返回的对象时,返回对常量的引用
6.32
int &get(int *array, int index) {return array[index];}
注意return处不要加&,否则会变成返回 array[ index ] 的地址
6.36(返回数组的引用的函数)
string (&func( )) [10];
&func( )表示函数的返回结果是一个引用
(&func( )) [10]表示引用的对象是一个维度为10的数组
string (&func( )) [10]表示数组的元素是string对象
6.37
直接编写返回数组引用的函数比较繁琐,使用类型别名,尾置返回类型和decltype关键字都可以简化
- 使用类型别名
typedef string arr[10]//using arr = string[10];arr& func( );
- 使用尾置返回类型
auto func( ) -> string(&) [10];
- 使用delctype关键字
string str[10];decltype(str) &func( );
返回数组指针
- 使用类型别名
arrT是含有10个整数的数组的别名,func返回一个指向含有10个整数的数组的指针
typedef int arrT[10];//using arrt = int[10];arrT* func(int i);
- 声明一个返回数组指针的函数(不使用类型别名)
定义返回数组指针的函数,数组的维度必须在形参列表的后面
例子:int (*func( int i )) [10] ;
的逐层理解
func(int i)
调用func函数需要一个int类型的实参
(*func( int i ))
可以对函数的调用结果解引用,所以必须加括号,否则返回指针的数组
(*func( int i )) [10]
解引用func的调用得到一个大小为10的数组
int (*func( int i )) [10]
表示数组中的元素是int类型 - 使用尾置返回类型
尾置返回类型跟在形参列表的后面并且以一个 -> 符号开头,函数真正返回类型在形参列表之后,原本防止返回类型的地方放置auto
注意解引用运算符加了括号
//func节后一个int类型的实参,返回指向含有10个整数的数组的指针auto func(int i) -> int (*) [10]
- 使用decltype
在已知函数返回的指针指向那个数组的情况下可以用decltype关键字声明返回类型
int odd[10] = {1, 3, 5, 7, 9};decltype(odd) *arrPtr(int i);
arrPtr使用关键字decltype表示它的返回类型是一个指针,且指针指向的对象和odd类型一致。decltype并不负责把数组类型转换为对应的指针,decltype的结果是一个数组,必须在函数声明的时候加一个*符号表示arrPtr返回指针。
6.43(内联函数和constexpr函数)
内联函数和constexpr函数通常定义在头文件内,以保证多个定义完全一致,同时内联函数的定义应该对编译器来说是可见的,编译器才能在调用点内联展开函数的的代码
- 内联函数,在函数的返回类型前面加 inline ,但是该请求编译器可以忽略
- constexpr函数,能用于常量表达式的函数,constexpr函数额外规定:函数的返回类型和所有形参的类型都是字面值类型;函数体有且只有一条return语句。
constexpr int new_sz() { return 42;};constexpr int foo = new_sz();
- constexpr函数不一定返回常量表达式,即允许constexpr函数的返回值不是常量
const xize_t scale( size_t cnt) { return new_sz() * cnt; }
scale(2)
返回常量表达式,int i = 2, scale(i)
返回的不是常量表达式
6.47(调试器 6.5.3节)
assert 预处理宏
assret(espr),表达式为假,assert输出信息并终止程序
NDEBUG
如果定义了NDEBUG,则assert什么也不做
使用 #define 语句定义NDEBUG,可以关闭调试状态
- 可以使用NDEBUG编写自己的条件调试代码,NDEBUG未定义就会执行
#ifndef
和#endif
之间的代码,NDEBUG已定义这些代码会被忽略:
此处如果打开调试器,就会执行中间的内容,关闭调试器则不会
#ifndef NDEBUG//……………………#endif
6.49(候选函数和可行函数 6.6节)
- 函数匹配第一步:选出调用对应的重载函数集,集合内的就是候选函数
- 函数匹配第二步:候选函数中选出可以被这组实参调用的函数,即可行函数(形参和实参数量相等,实参和形参类型相同或者可以转化)
6.52(实参类型转换 6.6.1)
实参类型到形参类型转换的等级排序:
6.54(函数指针 6.7节)
- 函数的类型由返回类型和形参类型共同决定,例如指向函数类型
bool (const string &, const string &)
的函数指针:bool (*pf)(const string &, const string &)
:
pf 前面由*表示 pf 是一个指针,右侧形参列表表示 pf 指向的是函数,左侧的是函数的返回类型
如果没有括号变成bool *pf(const string &, const string &)
,pf 就是一个返回bool指针的函数 - 如果把函数名作为一个值使用,它就会自动地转换成指针
bool lengthCompare(const string &, const string &)pf = lengthCompare;//取地址符是可选的//pf = &lengthCompare
- 直接用指针调用函数,不需要提前解引用,以下三个调用等价
bool b1 = pf("sadad", "dsada");bool b1 = (*pf)("sadad", "dsada");bool b1 = lengthCompare("sadad", "dsada");
- 可以为函数指针赋值nullptr或0,表示不指向任何一个函数:
pf = nullptr
- 重载函数指针举例
void ff(int*);void ff(unsigned int);void (*pf)(unsigned int) = ff//pf指向ff(unsigned int)
- 函数指针可以作为形参使用,形参虽然看起来是函数类型但是实际上被当成指针使用
void f(bool pf(const string &, const string &));//void f(bool (*pf)(const string &, const string &))f(lengthCompare);//直接把函数作为实参,其会自动转换成指针
简化函数指针(类型别名 或者 decltype)
重点注意:
①函数类型,但是在作为形参的化会自动转换为指针
②decltype返回的函数类型不会自动转化成指针类型,需要在结果上加上*
③和形参不同,函数返回类型不会自动转换成指针
- 此处的func1只是函数类型,但是在作为形参的化会自动转换为指针
//func是函数类型typedef decltype(lengthCompare) func1;//等价typedef bool func1(const string &, const string &));
- decltype返回的函数类型不会自动转化成指针类型,需要在结果上加上*
typedef decltype(lengthCompare) *func2;//等价typedef bool (*func2)(const string &, const string &));
- 重新声明f:把
void f(bool pf(const string &, const string &));
改为如下
void f(fun1);//等价void f(fun2);
返回指向函数的指针
- 声明返回函数指针的函数最简单的办法是使用类型别名
using F = int(int *, int);using PF = int(*)(int *, int);
- 和形参不同,返回类型不会自动转换成指针,我们必须显示第将返回类型指定为指针
PF f1(int);F *f1(int);
- 直接声明,不用类型别名
int (*f1(int)) (int *, int);
- 尾置返回类型
auto f1(int) -> int(*)(int *, int);
- decltype:牢记decltype返回函数类型而不是指针类型
bool lengthCompare(const string &, const string &);deltype(lengthCompare) *getFcn(const string &);vector<deltype(lengthCompare) *> vL;
一个元素是指向bool (const string &, const string &)
的指针的vector
C++Primer第五版 第六章 课后习题答案相关推荐
- 线性代数第五版吉尔伯特课后答_线性代数同济第五版第六章课后习题答案!
搜集 | 整理 | 测试 | @小愉 免责声明:以下资源或软件均来自互联网,仅供学习和交流使用,如有侵权请联系删除,请勿用于商业和非法途径等,如有法律纠纷与本人无关! 本文未经允许,不得转载! 适用 ...
- 线性代数第五版吉尔伯特课后答_线性代数同济第五版第四章课后习题答案!
搜集 | 整理 | 测试 | @小愉 免责声明:以下资源或软件均来自互联网,仅供学习和交流使用,如有侵权请联系删除,请勿用于商业和非法途径等,如有法律纠纷与本人无关! 本文未经允许,不得转载! 适用 ...
- 计算机网络原理(谢希仁第八版)第六章课后习题答案
第六章 1.互联网的域名结构是怎样的?它与目前的电话网的号码结构有何异同之处? 答:(1)域名的结构由标号序列组成,各标号之间用点隔开:- 三级域名. 二级域名. 顶级域名,各标号分别代表不同级别的域 ...
- 数据库系统概论(第五版) 王珊 第二章课后习题答案
1 .试述关系模型的三个组成部分. 答:关系模型由关系数据结构.关系操作集合和关系完整性约束三部分组成. 2 .试述关系数据语言的特点和分类. 答:关系数据语言可以分为三类: 关系代数语言. 关系演算 ...
- java第二版课后题答案_Java语言程序设计第2版第16章 课后习题答案
<Java语言程序设计第2版第16章 课后习题答案>由会员分享,可在线阅读,更多相关<Java语言程序设计第2版第16章 课后习题答案(62页珍藏版)>请在人人文库网上搜索. ...
- 《Python语言程序设计》王恺 机械工业出版社 第六章课后习题答案
第六章 字符串 6.5 课后习题 (1)Python 中,创建字符串时,可以使用单引号.双引号和三引号 (2)Python 中,使用字符串的 split 方法可以按照指定的分隔符对字符串进行切割,返回 ...
- C++ Primer(第五版)第七章 类 部分答案
第七章 类 练习7.2 练习7.3 练习7.4 练习7.6 练习7.7 练习7.9 练习7.14.7.15.7.22 练习7.23.7.24.7.26 练习7.27 练习7.2 曾在 2.6.2 节的 ...
- 数据库系统概论(第五版) 王珊 第六章课后习题答案
1 .理解并给出下列术语的定义: 函数依赖.部分函数依赖.完全函数依赖.传递依赖.候选码.主码.外码.全码(All 一key ).1 NF .ZNF .3NF .BcNF .多值依赖.4NF . 定义 ...
- 数据库概论(第五版)第六章课后习题答案(现更)
2. 语义:一个系有若干专业,每个专业每年只招一个班,每个班有若干学生.一个系的学生住在同一宿舍区.每个学生可参加若干学会,每个学会有若干学生.学生参加某学会有一个入会年份. 模式1:学生(U, F) ...
- C程序设计(谭浩强版)第六章课后习题
1.用筛选法求100之内的素数 筛选法:古希腊人将1-N个的数字写在一个板子上,然后逐一判断该数符不符合要求,不符合要求得数字就会划掉,知道将数字全部比对结束,比对完后因为板子上划掉得标记太多禄,像筛 ...
最新文章
- PHP流程控制语句例题,第四堂、php流程控制语句
- 根据开源数据库选择合适的工具
- 用Linux构建高效FTP服务器(2)
- 人工智能的炒作_解密人工智能:是炒作还是我们期望太高
- day32—CSS多列布局学习
- this的用法this.name=name 这个什么意思
- ElasticSearch6.x 7.x Elasticdump 在线安装、离线安装
- 百度坐标转换API使用
- cakephp 1.3 Views
- js designMode contentEditable 编辑在线网页
- java中的socket模型_Socket通信模型
- uv转化率多少正常_宣城UV光解设备价格多少-低价供应
- Python decorator
- OKapi BM25 算法介绍
- 如何使用Xpose绕过APP自定义证书验证去抓Https包
- 海康威视前端实习生面试
- 南京地图njmaps使用,以公众版为例
- 二分查找法--有序表
- discuz插入幻灯片_如何将符号插入Google文档和幻灯片
- 迅雷起死回生背后的男人,竟然是雷军
热门文章
- VS2013附加包含目录,添加相对路径
- ​​欧洲能源危机日益严重,这个冬天到底会有多 “冷” ?
- 干货 | PCB多层板为什么都是偶数层?奇数层不行吗?
- WireShark基本使用(5)第 5 章 文件输入/输出及打印
- windows 进程通信(使用DDE)
- 成长之路——发现问题、提出问题和解决问题
- 堆糖:爱豆图片分享社区
- char类型和字符串
- 微软云服务器怎么注销,如何关闭你的 Microsoft 帐户——注销微软账号
- 北京内推 | 微软亚洲研究院自然语言计算组招聘NLP研究实习生