cppPrimer第六章习题

6.1 实参和形参的区别是什么?

实参是形参的初始值,实参是调用函数时提供的,第一个实参初始化第一个形参,第二个实参初始化第二个形参,以此类推。

6.2 指出下列函数哪个有错误,为什么?应该如何修改这些错误?
(a) //实际返回值和指定返回值不一致
int f()
{
    string s;
    return s;
}
(b)//没有给出返回值
f2(int i){/*....*/}

(c)//形参不可以重名,且函数体漏了{
int calc(int v1,int v1)/**/}

(d)//函数体需要用花括号{}括起来
double square(double x) 
    return x*x;

修改后:

(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.6 说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式

形参是一种静态对象函数开始时为形参申请存储空间因为形参定义在函数的形参列表里,所以一旦函数终止,形参也就被销毁。

局部变量定义在代码块内,并且生命周期只存在于块执行期间

局部静态变量是定义在函数体块之内的,并且生命周期贯穿函数调用即之后时间。但是其作用域仍然现定于所定义的函数之内。且局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且知道程序终止时才被销毁。

例子:

size_t p6_6(int n) //n为形参
{
	static size_t cnt = 0; // 局部静态变量
	int num = n;
	int res = 1;//局部变量
	for (; n > 1; --n)
		res *= n;
	cout << num << "! = " << res << endl;
	return ++cnt;
}
void test01()
{
	int num;
	int cnt;
	while (cin >> num)
	{
		cnt = p6_6(num);
		cout << "计算了" << cnt << "次" << endl;
	}
}
6.13 假设T是某种类型名字,说明以下两个函数声明的区别:一个void f(T),另一个是 void f(T &)

void f(T)是普通的形参,修改形参的值不会影响到对应的实参

void f(T&),参数是引用形参,引用形参会绑定传入的实参(需为可修改的左值),修改引用形参的值即对实参进行修改。

6.14 举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。
  1. 交换两个整数的函数,形参应该是引用
void swap(int &lhs,int &rhs)
{
	int temp = lhs;
    lhs = rhs;
    rhs = temp;
}

当实参是右值的时,形参不能为引用类型

int add(int a, int b)
{
	return a+b;
}
int main()
{
    int res = add(1, 2);
	return 0;
}
6.15 说明find_char 函数中的三个形参为什么是现在的类型,特别说明为什么s是常量引用,而occurs是普通引用?为什么s和occurs是引用类型而c不是?如果令s是普通引用会发生什么情况?如果令occurs是常量引用会发生什么情况?
string::size_type p6_15(const string &s,char c, string::size_type &occurs)
{
	auto ret = s.size(); //第一次出现的位置
	occurs = 0;
	for (decltype(ret) i = 0; i != s.size(); ++i)
	{
		if (s[i] == c)
		{
			if (ret == s.size())//表明是第一次出现
				ret = i;

			++occurs;
		}
	}
	return ret;
}

形参字符串s可能比较长若用传值参数可能拷贝需要比较大的开销,并且字符串s不需要改变,故可以为常量引用类型。

occurs需要统计字符c出现的次数,由于返回值是返回了第一次出现的index,故通过引用来做到返回额外的这个次数的信息

形参c可能为右值(引用无法绑定右值右值),并且c本身为char类型,大小就比较小,故用普通形参即可

令s为普通引用可能在操作中对s进行误修改

令occurs为常量引用会导致没法递增,从而无法正确统计出现次数

6.16 下面这个函数虽然合法,但不算特别有用。指出它的局限性并设法改善。
bool is_empty(string &s) {return s.empty();}

该函数只能接受非常量的实参对象,不能够接受字符串字面值,字符串常量以及常量引用和需要类型转换的对象

修改为:

bool is_empty(const string &s){return s.empty();}
6.18 为下面函数编写函数声明,从给定的名字中推测函数具备的功能

(a)名为compare的函数,返回布尔值,两个参数都是matrix类的引用。

(b)名为change_val的函数,返回vector的迭代器,有两个参数:一个是int,另一个是vector的迭代器

bool compare(const matrix &cm1, const matrix &cm2);

vector<int>::iterator change_val(int n, vector<int>::iterator vi);
6.20 引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用会发生什么情况?
  1. 当对形参只读的时候或者希望能接受实参为字面值,常量引用,常量对象以及需要类型转换的对象时,应该用常量引用
  2. 可能会造成误修改引用所绑定的对象,或者是不能接受一些类型的实参:字面值,常量引用、常量对象。
6.24 描述下面函数行为,如果代码中存在问题,请指出并改正
void print(const int ia[10])
{
    for(size_t i = 0;i != 10;++i)
        cout<<ia[i]<<endl;
}

所给的实参并不一定是一个指向长度为10的int数组的指针,需要再提供数组的长度

void print(const int ia[10], size_t size)
{
    for(size_t i = 0;i != size;++i)
        cout<<ia[i]<<endl;
}
6.31 什么情况下返回的引用无效?什么情况下返回的常量引用无效?

返回的是一个局部对象的引用则无效,因为局部对象在函数结束后就被释放

希望通过函数返回值进行修改或调用时,返回常量引用无效。

6.32 下面函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。
int& get(int* array, int index)
{
	return array[index];
}

void test01()
{
	int ia[10];
	for (int i = 0; i < 10; ++i)
		get(ia, i) = i;

	for (const int& n : ia)
		cout << n << " ";
}

合法,其功能为返回数组下标为index的元素,且该返回值是一个引用,为左值可以用于接受赋值,绑定的返回值对象声明周期一直到get结束后仍存在,故合法。

6.36 编写一个函数声明,使其返回数组的引用并且该数组包含10个string对象。不要使用位置返回类型、decltype或类型别名。
string (&func(void))[10]
6.37 为上一题函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用decltype关键字。你觉得哪种形式最好,为什么?
typedef string arrT[10];
using  arrT = string[10];
arrT & func(void);

auto func(void) -> string (&)[10];

string arr[10];
decltype(arr)& func(void);
6.38 修改arrPtr函数,使其返回数组的引用
int odd[] = { 1, 3, 5, 7, 9 };
int even[] = { 2, 4, 6, 8, 10 };

decltype(odd)& arrRef(int i)
{
	return (i % 2) ? odd : even;
}
6.41 下面哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员初衷不符,为什么?
char *init(int ht, int wd = 80, char bckgrnd = ' ');
(a)init();
(b)init(24, 10);
(c)init(14, '*');

(a) 是非法的,因为没有给形参ht指定的实参

(b)合法,调用init(24, 10, ’ ');

©合法,但与程序员初衷不符,实际调用的是init(14,’*’,’ ');

编译器会将char类型’*'转换为int类型对应数据。本意应该是想调用init(14, 80, ’ ');

6.43 你会把下面哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
(a) inline bool eq(const BigInt&, const BigInt&){....};
(b) void putValues(int *arr, int size);

(a)的定义放在头文件中,因为对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致,故内联函数和constexpr函数通常定义在头文件中。

(b) 普通的函数声明放在头文件中,方便其他文件包含,定义写在源文件中。

6.46 能否把下方isShorter函数定义成constexpr函数吗?如果能,将它改写成constexpr函数,如果不能,说明原因
bool isShorter(const string& s1, const string& s2)
{
	return s1.size() < s2.size();
}

解答2:不可以,因为std::string::size()不是constexpr函数,constexpr函数的返回值类型以及所有形参都得是字面值类型。

6.48 说明下面这个循环的含义,它对assert的使用合理吗?
string s;
while(cin>>s && s != sought)
{}
assert(cin);

该循环对合法输入且不为sought的字符串进行处理,并且若最后输入不为sought而为结束字符则退出程序,否则继续向下执行,但assert使用不大合理,应该为assert(s == sought)

6.49 什么是候选函数,什么是可行函数?

候选函数是本次调用对应的重载函数集,具有两个特征:1.与被调用函数同名 2.其声明在调用点处可见

可行函数有两个特征:1.形参数量与本次调用提供的实参数量相等 2.每个实参的类型与对应形参类型相同,或者实参可以转换为形参的类型

6.50 已知有以下对函数f的声明,对于下面每一个调用列出可行函数。其中哪个函数是最佳匹配?如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?
void f();
void f(int);
void f(int, int);
void f(double, double = 3.14);

有以下的调用

(a) f(2.56, 42)		(b)f(42)	  (c)f(42,0) 	(d)f(2.56, 3.14)

(a)可行函数为 void f(int, int);和 void f(double, double = 3.14),

调用不合法因为对第二个实参来说 void f(int, int);更好,而对第一个参数 void f(double, double = 3.14)更好,编译器最终因为这个调用有二义性拒绝请求。

(b)可行函数为 void f(int) 和 void f(double, double = 3.14),其中 void f(int)为最佳匹配

(c)可行函数为 void f(int, int);和 void f(double, double = 3.14),最佳匹配为:void f(int, int);

(d)可行函数为 void f(int, int);和 void f(double, double = 3.14),最佳匹配为:void f(double, double = 3.14),因为对第一个和第二个实参来说,都不需要进行类型转换。

6.52 已知有如下声明:

void manip(int, int);

double dobj;

请指出下列调用中每个类型的转换等级

(a) manip(‘a’, ‘z’);

(b)manip(55.4, dobj);

manip(‘a’ ,‘z’);中实参是通过类型提升(char -> int)实现的匹配为第3级

manip(55.4, dobj);调用中,实参是通过算术类型转换(double -> int)实现匹配,为第4级

6.53 说明下列每组声明的第二条语句会产生什么影响,并指出哪些不合法
(a)int calc(int&,int&);
    int calc(const int&,const int&); // 形参是常量引用
(b)int calc(char *,char *);
    int calc(const char*, const char*); //形参精确匹配时指向字符常量的指针
(c)int calc(char*, char *);
    int calc(char* const, char* const); //不合法,两个形参分别为顶层const,编译器忽略顶层const,故两个声明重复
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值