《C++ Primer》第6章 6.5节习题答案

《C++ Primer》第6章 函数

6.5节 特殊用途语言特性

练习6.40:下面的哪个声明是错误的?为什么?
(a) int ff(int a, int b = 0, int c = 0);
(b) char *init(int ht = 24, int wd, char bckgrnd);
【出题思路】
如果函数的某个形参在多次调用中都被赋予了同一个值,则我们可以把这个反复出现的值定义为默认实参。C++对于默认实参在参数列表中可能出现的位置有明确规定。
【解答】
在上面的两个声明中,(a)是正确的而(b)是错误的。它们都用到了默认实参,但是C++规定一旦某个形参被赋予了默认实参,则它后面的所有形参都必须有默认实参。这一规定是为了防范可能出现的二义性,显然(b)违反了这一规定。

练习6.41:下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?
char *init(int ht, int wd = 80, char bckgrnd=' ');
(a)init();        (b)init(24, 10);        (c)init(14, '*');
【出题思路】
要想使用默认实参,只需要在调用函数时省略该实参就可以了。实参按照其位置解析,默认实参负责填补函数调用缺少的尾部实参。
【解答】
(a)是非法的,该函数有两个默认实参,但是总计有三个形参,其中第一个形参并未设定默认实参,所以要想调用该函数,至少需要提供一个实参。
(b)是合法的,本次调用提供了两个实参,第一个实参对应第一个形参ht,第二个实参对应第二个形参wd,其中wd的默认实参没有用到,第三个形参bckgrnd使用它的默认实参。
(c)在语法上是合法的,但是与程序的原意不符。从语法上来说,第一个实参对应第一个形参ht,第二个实参的类型虽然是char,但是它可以自动转换为第二个形参wd所需的int类型,所以编译时可以通过,但这显然违背了程序的原意,正常情况下,字符'*'应该被用来构成背景。

练习6.42:给make_plural函数(参见6.3.2节,第201页)的第二个形参赋予默认实参's',利用新版本的函数输出单词success和failure的单数和复数形式。
【出题思路】
对于英语单词来说,大多数名词的复数是在单词末尾加's'得到的,也有一部分名词在单数转变为复数时需要在末尾加'es'。我们可以把's'作为默认实参,大多数情况下不必考虑这个参数,只有在遇到末尾是'es'的单词时才专门处理。
【解答】
满足题意的程序如下所示:

#include <iostream>
#include <string>

using namespace std;

//最后一个形参赋予了默认实参
string mak_plural(size_t ctr, const string &word, const string &ending = "s")
{
    return (ctr > 1) ? word + ending : word;
}

int main()
{
    cout << "success 的单数形式是:" << mak_plural(1, "success", "es") << endl;
    cout << "success 的单数形式是:" << mak_plural(2, "success", "es") << endl;
    
    //一般情况下调用该函数只需要两个实参
    cout << "failure 的单数形式是:" << mak_plural(1, "failure") << endl;
    cout << "failure 的单数形式是:" << mak_plural(2, "failure") << endl;
    return 0;
}

运行结果:

 练习6.43:你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
(a) inline bool eq(const BigInt& const BigInt&) { ... }
(b) void putValues(int *arr, int size);
【出题思路】
函数的声明应该放在头文件中,同时内联函数的定义也应该放在头文件中。
【解答】
(a)应该放在头文件中。因为内联函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开该函数的代码,所以仅有函数的原型不够。并且,与一般函数不同,内联函数有可能在程序中定义不止一次,此时必须保证在所有源文件中定义完全相同,把内联函数的定义放在头文件中可以确保这一点。
(b)是函数声明,应该放在头文件中。

练习6.44:将6.2.2节(第189页)的isShorter函数改写成内联函数。
【出题思路】
只需要在普通函数的前面加上关键字inline,就可以将该函数设置为内联了。内联函数在编译时展开,从而消除了调用函数时产生的开销。
【解答】
改写后的内联函数是:
inline bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

练习6.45:回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗?如果是,将它们改写成内联函数;如果不是,说明原因。
【出题思路】
决定一个函数是否应该是内联函数有很多评判的依据。一般来说,内联机制适用于规模较小、流程直接、频繁调用的函数。一旦函数被定义成内联的,则在编译阶段就展开该函数,以消除运行时产生的额外开销。如果函数的规模很大(比如上百行)不利于展开或者函数只被调用了一两次,那么这样的函数没必要也不应该是内联的。
【解答】
在本章前面实现的函数中,大多规模较小且流程直接,适合于设置为内联函数;如果以后遇到一些代码行数较多的函数,就不适合了。举两个例子:
练习6.11中的reset函数改写后的形式是:
inline void reset(int &l)
{
    i = 0;
}
练习6.21中的myCompare函数改写后的形式是:
inline int myCompare(const int val, const int *p)
{
    return (val > *p) ? val: *p;
}

 

练习6.46:能把isShorter函数定义成constexpr函数吗?如果能,将它改写成constexpr函数;如果不能,说明原因。
【出题思路】
constexpr函数是指能用于常量表达式的函数,constexpr函数的返回类型和所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。
【解答】
显然isShorter函数不符合constexpr函数的要求,它虽然只有一条return语句,但是返回的结果调用了标准库string类的size()函数和<比较符,无法构成常量表达式,因此不能改写成constexpr函数。

练习6.47:改写6.3.2节(205页)练习中使用递归输出vector内容的程序,使其有条件地输出与执行过程有关的信息。例如,每次调用时输出vector对象的大小。分别在打开和关闭调试器的情况下编译并执行这个程序。
【出题思路】
本题旨在考查如何在程序中打开和关闭调试器。
【解答】
满足题意的程序如下所示:

#include <iostream>
#include <vector>

using namespace std;
//递归函数输出vector<int>的内容
void print(vector<int> vInt, unsigned index)
{
    unsigned sz = vInt.size();
    //设置在此处输出调试信息
    #ifndef NDEBUG
    cout << "vector对象的大小是:" << sz << endl;
    #endif //NDEBUG
    if(!vInt.empty() && index < sz)
    {
        cout << vInt[index] << endl;
        print(vInt, index + 1);
    }
}

int main()
{
    vector<int> v = {1,3,5,7,9,11,13,15};
    print(v, 0);
    return 0;
}

运行结果:

 

练习6.48:说明下面这个循环的含义,它对assert的使用合理吗?
string s;
while(cin >> s && s != sought) {  } //空函数体
assert(cin);
【出题思路】
assert是一种预处理宏,当assert的条件为真时什么也不做,当它的条件为假时输出信息并终止程序。
【解答】
该程序对assert的使用有不合理之处。在调试器打开的情况下,当用户输入字符串s并且s的内容与sought不相等时,执行循环体,否则继续执行assert(cin);语句。换句话说,程序执行到assert的原因可能有两个,一是用户终止了输入,二是用户输入的内容正好与sought的内容一样。如果用户尝试终止输入(事实上用户总有停止输入结束程序的时候),则assert的条件为假,输出错误信息,这与程序的原意是不相符的。当调试器关闭时,assert什么也不做。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值