【C++ Primer】第六章 函数 (练习)

C++ Primer 5th 随堂练习

【C++ Primer】第一章 开始 (练习)

【C++ Primer】第二章 变量和基本类型 (练习)

【C++ Primer】第三章 字符串、向量和数组 (练习)

【C++ Primer】第四章 表达式 (练习)

【C++ Primer】第五章 语句 (练习)

【C++ Primer】第六章 函数 (练习)

【C++ Primer】第七章 类 (练习)

【C++ Primer】第八章 IO 库 (练习)

【C++ Primer】第九章 顺序容器 (练习)

【C++ Primer】第十章 泛型算法 (练习)

【C++ Primer】第十一章 关联容器 (练习)


第六章 函数


练习 6.1

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

解答

形参出现在函数定义的地方,形参列表可以包含 0 个或任意个形参,多个形参之间以逗号分隔,各形参均需明确类型。形参规定了一个函数所接受数据的类型和数量。

实参出现在函数调用的地方,实参的数量通常与形参一样多。实参的主要作用是初始化形参,并且这种初始化过程是一一对应的,即第一个实参初始化第一个形参、第二个实参初始化第二个形参,依此类推。实参的类型必须与对应的形参类型匹配。


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

解答

// 函数类型与返回类型不匹配, 按原意应修改函数类型为 string:
(a) string f() {
         string s;
         // ...
         return s;
   }

// 未定义函数类型, 按原意应增加函数类型为 void:
(b) void f2(int i) { /* ... */ }

// 函数形参名不可以重复, 按原意修改其中一个形参名为 v2:
(c) int calc(int v1, int v2) { /* ... */ }

// 函数体没有包含在花括号内, 按原意应增加花括号:
(d) double square (double x)  {return x * x;}

练习 6.3

编写你自己的 fact 函数,上机检查是否正确。注:阶乘。

解答

源程序 - 迭代实现:

#include <iostream>
#include <stdexcept>

using namespace std;

int fact (int val)
{
    if (val < 0)
        throw runtime_error ("Input cannot be a negative number");  // 输入负数抛出异常

    int ret = 1;
    while (val > 1) 
        ret *= val--;
    return ret;
}

int main()
{   
    int num;
    cin >> num;
    int result = fact(num);
    cout << result << endl;

    system("pause");
    return 0;
}


练习 6.4

编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在 main 函数中调用该函数。

解答

源程序 - 递归实现:

#include <iostream>
#include <stdexcept>

using namespace std;

int fact (int val)
{   // return val > 1 ? (val * fact(val-1)) : 1;
    if (val == 0)
        return 1;
    else
        return val * fact(val-1);
}

int main()
{   
    int num;
    cout << "input a integer to compute factorial: ";
    cin >> num;
    if (num < 0)
        throw runtime_error ("input cannot be a negative number");
    cout << "the factorial of " << num << " is " << fact(num) << endl;

    system("pause");
    return 0;
}

练习 6.5

编写一个函数输出其实参的绝对值。

解答

#include <iostream>

using namespace std;

int abs(int val)
{
    return val < 0 ? -val : val;
}

int main()
{   
    int num;
    cout << "input a integer to compute absolute value: ";
    cin >> num;
    cout << "the absolute value of " << num << " is " << abs(num) << endl;

    system("pause");
    return 0;
}


练习 6.6

说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时达到这三种形式。

解答

// 举例
#include <iostream>

using namespace std;

int count_add(int n)                   // n 是形参
{
    static int ctr = 0;                // ctr 是局部静态变量
    ctr += n;
    return ctr;
}

int main()
{
    for (int i = 0; i != 10; ++i)      // i 是局部变量
        cout << count_add(i) << endl;

    system("pause");
    return 0;
}

练习 6.7

编写一个函数,当它第一次被调用时返回 0,以后每次被调用返回值加1。

解答

#include <iostream>

using namespace std;

int func()
{
    static int cnt = 0;  // 局部静态变量
    return (cnt++);
}

int main()
{   
    for (int i = 0; i < 3; ++i)
        cout << func() << endl;

    system("pause");
    return 0;
}

输出:

0
1
2

练习 6.8 - Chapter6.h

编写一个名为 Chapter6.h 的 头文件,令其包含 6.1 节练习中的 函数声明

解答

// Chapter6.h
#ifndef CHAPTER6_H_INCLUDE
#define CHAPTER6_H_INCLUDE

// function prototype
int fact(int );
int abs(int );
double myABS(double );
double myABS2(double );

#endif  // CHAPTER6_H_INCLUDE

练习 6.9 - fact.cc | factMain.cc

编写你自己的 fact.cc 和 factMain.cc ,这两个文件都应该包含上一小节的练习中编写的 Chapter6.h 头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。

解答

fact() 函数定义于 fact.cc 文件中,声明于 Chapter6.h 文件中,并在 factMain.cc 文件中创建 main() 函数实现对 fact() 函数的调用。

// fact.cc
#include "Chapter6.h"
using namespace std;

int fact(int val)
{   // return val > 1 ? (val * fact(val-1)) : 1;
    if (val == 0)
        return 1;
    else
        return val * fact(val-1);
}
// factMain.cc
#include <iostream>
#include "Chapter6.h"
using namespace std;

int main()
{   
    int num;
    cout << "input a integer to compute factorial: ";
    cin >> num;
    if (num < 0)
        throw runtime_error ("input cannot be a negative number");
    cout << "the factorial of " << num << " is " << fact(num) << endl;

    system("pause");
    return 0;
}

练习 6.10

编写一个函数,使用指针形参交换两个整数的值。 在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。

解答

源程序 - (使用指针形参):

#include <iostream>
using namespace std;

int swap(int *x, int *y)
{
    cout << "before swap: " << *x << " " << *y << endl;
    int temp = *x;  // 注意交换指针所指对象值, 而非交换地址
    *x = *y;
    *y = temp;
    cout << "after swap: " << *x << " " << *y << endl;
}

int main()
{   
    int a = 1, b = 2;
    swap(&a, &b);  // 注意传入实参地址, 而非实参对象
    cout << "now: " << a << " " << b << endl;

    system("pause");
    return 0;
}

输出:

before swap: 1 2
after swap: 2 1
now: 2 1


练习 6.11

编写并验证你自己的 reset 函数,使其作用于引用类型的参数。注:reset 即置 0。

解答

#include <iostream>
using namespace std;

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

int main()
{   
    int a = 1;
    reset(a);  // 注意传入实参地址, 而非实参对象
    cout << a << endl;

    system("pause");
    return 0;
}

练习 6.12

改写 6.2.1 节练习 (对比练习 6.10) 中的程序,使其引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?

解答

源程序 - (使用引用形参替代指针形参):

#include <iostream>
using namespace std;

int swap(int &x, int &y)
{
    cout << "before swap: " << x << " " << y << endl;
    int temp = x;
    x = y;
    y = temp;
    cout << "after swap: " << x << " " << y << endl;
}

int main()
{   
    int a = 1, b = 2;
    swap(a, b);
    cout << "now: " << a << " " << b << endl;

    system("pause");
    return 0;
}

输出:

before swap: 1 2
after swap: 2 1
now: 2 1

可见,使用引用形参要比指针形参更具简洁性、易用性和可读性,例如函数定义和函数调用需要注意的细节更少了,无需额外声明指针变量,也避免了拷贝指针的值


练习 6.13

假设 T 是某种类型的名字,说明以下两个函数声明的区别: 一个是 void f(T),另一个是 void f(&T)。

解答


练习 6.14

举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。

解答

 


练习 6.15

说明 find_char 函数中的三个形参为什么是现在的类型,特别说明为什么 s 是常量引用而 occurs 是普通引用? 为什么 s 和 occurs 是引用类型而 c 不是? 如果令 s 是普通引用会发生什么情况? 如果令 occurs 是常量引用会发生什么情况?

解答


练习 6.16

下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。

bool is_empty(string& s) { return s.empty(); }

解答

is_empty 函数的形参 s 使用了 string & 类型,使之 只能接受 string 对象作为实参。如果下面这样调用是非法的:

const string str = "hello;
bool flag = is_empty(str);      // 非法
bool flag = is_empty("hello");  // 非法

若改为 const string & 类型,则更具泛化性,可接受 const string 对象string 对象字面值 乃至 其他需要类型转换的对象。如下所示:

bool is_empty(const string& s) { return s.empty(); }

练习 6.17

编写一个函数,判断 string 对象中是否含有大写字母。 编写另一个函数,把 string 对象全部改写成小写形式。 在这两个函数中你使用的形参类型相同吗?为什么?

解答

源程序:

#include <iostream>
#include <string>
using namespace std;

bool has_upper(const string &str)  // 常量引用形参 - 参数值只读
{
    for (auto c : str) {
        if (isupper(c)) 
            return true;
    }
    return false;
}

void upper_to_lower(string &str) {  // 非常量引用形参 - 参数值读写
    for (auto &c : str) {
        if (isupper(c))
            c = tolower(c);
    }
}

int main()
{   
    // 朴素的交互测试
    string str;
    cin >> str;
    cout << has_upper(str) << endl;
    upper_to_lower(str);
    cout << str << endl;

    system("pause");
    return 0;
}

控制台交互:

String
1
string

练习 6.18

为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。

(a) 名为 compare 的函数,返回布尔值,两个参数都是 matrix 类的引用。
(b) 名为 change_val 的函数,返回 vector 的迭代器,有两个参数:一个是 int,另一个是 vector 的迭代器。

解答

// 函数声明别忘了分号
(a) bool compare(const matrix &x, const matrix &y);
(b) vector<int>::iterator change_val(int, vector<int>::iterator); // 更难些

练习 6.19

假定有如下声明,判断哪个调用合法、哪个调用不合法。对于不合法的函数调用,说明原因。

double calc(double);
int count(const string &, char);
int sum(vector<int>::iterator, vector<int>::iterator, int);
vector<int> vec(10);
(a) calc(23.4, 55.1);
(b) count("abcda",'a');
(c) calc(66);
(d) sum(vec.begin(), vec.end(), 3.8);

解答

  • (a):非法,实参数量与形参列表包含的形参数量不匹配;
  • (b):合法,字面值常量可以作为常量引用形参的值;
  • (c):合法,调用函数时会发生隐式类型转换;
  • (d):合法,调用函数时会发生隐式类型转换。

练习 6.20

引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况?

解答


练习 6.21

编写一个函数,令其接受两个参数:一个是 int 型的数,另一个是 int 指针。 函数比较 int 的值和指针所指的值,返回较大的那个。 在该函数中指针的类型应该是什么?

解答

由题可知,函数实际上比较的是第一个实参的值和第二个实参所指对象的值。因为两个参数的内容都不会被修改 (只读),所有指针类型应为 const int *

源程序:

#include <iostream>
using namespace std;

int larger_int(const int x, const int *y_ptr)
{
    int y = *y_ptr;
    return (x > y) ? x : y;
}

int main()
{   
    // 朴素的交互测试
    int a, b;
    cin >> a >> b;
    cout << "the larger integer is: " << larger_int(a, &b) << endl;

    system("pause");
    return 0;
}

 控制台交互:

9 5
the larger integer is: 9

练习 6.22

编写一个函数,令其交换两个 int 指针。

解答

源程序 (分别交换 指针所指对象的值指针的值):

#include <iostream>
using namespace std;

// 交换指针所指对象的值
void swap_ptr_value(int *x_ptr, int *y_ptr)
{
    int temp = *x_ptr;
    *x_ptr = *y_ptr;
    *y_ptr = temp;

}

// 交换指针自身的值, 即指针地址
void swap_ptr_address(int *(&x_ptr), int *(&y_ptr))  // 即便是指针, 要改变值还是要引用传值
{
    int *temp = x_ptr;
    x_ptr = y_ptr;
    y_ptr = temp;

}

int main()
{   
    int a = 2, b = 3;
    int *a_ptr = &a, *b_ptr = &b;  // 指针

    cout << "before swap value: " << a << " " << b << endl;
    swap_ptr_value(a_ptr, b_ptr);
    cout << "after swap value: " << a << " " << b << endl;

    cout << "before swap address: " << a_ptr << " " << b_ptr << endl;
    swap_ptr_address(a_ptr, b_ptr);
    cout << "after swap address: " << a_ptr << " " << b_ptr << endl;

    system("pause");
    return 0;
}

输出:

before swap value: 2 3
after swap value: 3 2
before swap address: 0x61fe3c 0x61fe38
after swap address: 0x61fe38 0x61fe3c

练习 6.23

参考本节介绍的几个 print 函数,根据理解编写你自己的版本。 依次调用每个函数使其输入下面定义的 i 和 j:

int i = 0, j[2] = { 0, 1 };

解答

源程序:

#include <iostream>
using namespace std;

// 参数是常量整型指针
void print1(const int *p)
{
    cout << *p << endl;
}

// 参数分别是常量整型指针和数组容量
void print2(const int *p, const int sz)
{
    for(auto i = 0; i != sz; ++i)
        cout << *p++ << " ";
    cout << endl;
}

// 参数分别是常量整型数组的首尾边界
void print3(const int *beg, const int *end)
{
    for(auto it = beg; it != end; ++it)
        cout << *it << " ";
    cout << endl;
}

// 参数分别是常量整型数组(首元素指针)和数组尺寸 (索引/下标)
void print4(const int arr[], size_t sz)
{
    for(size_t i = 0; i != sz; ++i)
        cout << arr[i] << " ";
    cout << endl;
}

int main()
{   
    int i = 0, j[2] = {0, 1};
    print1(&i);
    print1(j);
    print2(&i, 1);
    print2(j, sizeof(j)/sizeof(*j));  // 数组 j 的容量
    print3(begin(j), end(j));
    print4(j, end(j)-begin(j));  // 数组 j 的容量
    
    system("pause");
    return 0;
}

输出:

0
0
0
0 1
0 1
0 1

练习 6.24

描述下面这个函数的行为。如果代码中存在问题,请指出并改正。

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

解答

上述函数通过索引/下标依次输出整型常量数组 ia 各元素。但是,for 循环控制结构中,循环条件所使用的字面值 10 与实际输入数组的容量不一定一致。可以增加一个显式表示数组大小的形参 sz:

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


练习 6.25

编写一个 main 函数,令其接受两个实参。把实参的内容连接成一个 string 对象并输出出来。

解答

源程序 - (命令行参数): 

#include <iostream>
#include <string>
using namespace std;

int main(int argc, char **argv)
{
    string str;
    for (int i = 1; i != argc; ++i)   // 可以接受任意数量的实参了
        str += string(argv[i]) + " ";
    cout << str << endl;

    return 0;
}

命令行交互:

./test hello world
hello world


练习 6.26

编写一个程序,使其接受本节所示的选项;输出传递给 main 函数的实参内容。

解答

练习 6.25


练习 6.27

编写一个函数,它的参数是 initializer_list 类型的对象,函数的功能是计算列表中所有元素的和。

解答

源程序:

#include <iostream>
using namespace std;

int compute_sum(initializer_list<int> nums)
{   
    int sum = 0;
    for (auto n : nums)
        sum += n;
    return sum;
}

int main()
{   
    auto int_list = {1, 2, 3, 4, 5};
    cout << "the sum is: " << compute_sum(int_list) << endl;
    cout << "the sum is: " << compute_sum({6, 7, 8, 9, 10}) << endl;

    system("pause");
    return 0;
}

输出:

the sum is: 15
the sum is: 40

练习 6.28

在 error_msg 函数的第二个版本中包含 ErrCode 类型的参数,其中循环内的 elem 是什么类型?

解答

Initializer_list<string> 中所有元素都是 string 类型的,因此 const auto &elem : i1 推断得到的 elem 类型是 const string & 。使用引用是为了避免拷贝长字符串,把它定义为常量的原因是只需要读取字符串的内容而无需修改它 (只读即可,无需读写)。


练习 6.29

在范围 for 循环中使用 initializer_list 对象时,应该将循环控制变量声明成引用类型吗?为什么?

解答


练习 6.30

在编译第 200 页的 str_subrange 函数,看看你的编译器是如何处理函数中的错误的。

解答

编译错误信息:

test.cpp:16:7: error: return-statement with no value, in function returning ‘bool’ [-fpermissive]


练习 6.31

什么情况下返回的引用无效?什么情况下返回常量的引用无效?

解答


练习 6.32

下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。

int &get(int *array, int index) { return array[index]; }
int main()
{
    int ia[10];
    for (int i = 0; i != 10; ++i)
        get(ia, i) = i;
}

解答


练习 6.33

编写一个递归函数,输出 vector 对象的内容。

解答

源程序 - 方式一 (迭代器):

#include <iostream>
#include <vector>
using namespace std;

// 注意迭代器形参类型, 此处使用了常量迭代器类型
void print_vector(vector<int>::const_iterator beg, vector<int>::const_iterator end)
{   
    if (beg != end) {
        cout << *beg << " ";
        print_vector(++beg, end);
    }
}

int main()
{   
    vector<int> vec = {1, 2, 3, 4, 5};
    print_vector(vec.cbegin(), vec.cend());  // 常量迭代器类型
    cout << endl;

    system("pause");
    return 0;
}

源程序 - 方式二 (索引/下标):

#include <iostream>
#include <vector>
using namespace std;

// 使用数组及其当前元素索引作为形参
void print_vector(vector<int> vInt, unsigned index)
{   
    if (!vInt.empty() && index < vInt.size()) {
        cout << vInt[index] << " ";
        print_vector(vInt, index+1);
    }
}

int main()
{   
    vector<int> vec = {1, 2, 3, 4, 5};
    print_vector(vec, 0);
    cout << endl;

    system("pause");
    return 0;
}

输出:

1 2 3 4 5

练习 6.34

如果 factorial 函数的停止条件如下所示,将发生什么?

if (val != 0)

解答

若输入 val 为非负整数,则正常、正确输出;

若输入为负整数,则程序将因始终不满足终止条件而陷入无限递归,直至溢出。


练习 6.35

在调用 factorial 函数时,为什么我们传入的值是 val-1 而非 val-- ?

解答

若改为 val--,变量的递减操作和传值操作将共存于一条表达式中,可能导致产生未定义的值;换言之,将会永远传入相同的值来递归调用函数,递归将无限执行。


练习 6.36

编写一个函数声明,使其返回数组的引用并且该数组包含 10 个 string 对象。不用使用尾置返回类型、decltype 或者类型别名。

解答

string (&func())[10];


练习 6.37

为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用 decltype 关键字。 你觉得哪种形式最好?为什么?

解答

我觉得尾置返回类型最好,因为它最简洁。

typedef string str_arr[10];  
str_arr& fun();                // 类型别名
auto fun() -> string(&)[10];  // 尾置返回类型
string str_arr[10];
decltype(str_arr) &fun();  // decltype 关键字

 


练习 6.38

修改 arrPtr 函数,使其返回数组的引用。

解答

int odd[] = {1, 3, 5, 7, 9};
int even[] = {2, 4, 6, 8, 10};
// 返回一个引用, 该引用所引的对象是一个含有 5 个整数的数组
decltype(odd)& arrPtr(int i)
{
    return (i % 2) ? odd : even;  // 返回数组的引用
}

练习 6.39

说明在下面的每组声明中第二条语句是何含义。 如果有非法的声明,请指出来。

(a) int calc(int, int);
    int calc(const int, const int);
(b) int get();
    double get();
(c) int *reset(int *);
    double *reset(double *);

解答


练习 6.40

下面的哪个声明是错误的?为什么?

(a) int ff(int a, int b = 0, int c = 0);
(b) char *init(int ht = 24, int wd, char bckgrnd);	

解答

声明 (b) 是错误的,C++ 规定 某一形参被赋予了默认实参,则其后面的形参都必须有默认实参。这一规定是为了 防范可能出现的二义性,显然 (b) 违法了该规定。


练习 6.41

下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?

char *init(int ht, int wd = 80, char bckgrnd = ' ');
(a) init();
(b) init(24,10);
(c) init(14,'*');

解答


练习 6.42

给 make_plural 函数的第二个形参赋予默认实参 's',利用新版本的函数输出单词 success 和 failure 的单数和复数形式。

解答

源程序:

#include <iostream>
#include <string>
using namespace std;

string make_plural(size_t ctr, const string& word, const string& ending = "s")
{
    return (ctr > 1) ? word + ending : word;
}

int main()
{
    cout << "single: " << make_plural(1, "success", "es") << " "
         << make_plural(1, "failure") << endl;
    cout << "plural : " << make_plural(2, "success", "es") << " "
         << make_plural(2, "failure") << endl;

    system("pause");
    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 节的 isShorter 函数改写成内联函数。

解答

inline bool is_shorter(const string &lft, const string &rht) 
{
    return lft.size() < rht.size();
}


练习 6.45

回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗? 如果是,将它们改写成内联函数;如果不是,说明原因。

解答


练习 6.46

能把 isShorter 函数定义成 constexpr 函数吗? 如果能,将它改写成 constxpre 函数;如果不能,说明原因。

解答

不能。constexpr 函数指的是能用于常量表达式的函数,其返回值类型及所有形参都要求是字面值类型,而且函数体中必须有且只有一条 return 语句。


练习 6.47

改写 6.3.2 节练习中使用递归输出 vector 内容的程序,使其有条件地输出与执行过程有关的信息。例如,每次调用时输出 vector 对象的大小。分别在打开和关闭调试器的情况下编译并执行这个程序。

解答

源程序:

#include <iostream>
#include <vector>
using namespace std;

// 使用数组及其当前元素索引作为形参, 通过递归函数输出 vector<int> 的内容
void print_vec(vector<int> vec, unsigned index)
{   
    // 设置在此处输出调试信息
    #ifndef NDEBUG
        cout << "vector size: " << vec.size() << endl;
    #endif  // NDEBUG
    if (!vec.empty() && index < vec.size())
    {
        cout << vec[index] << endl;
        print_vec(vec, index+1);
    }
}

int main()
{   
    vector<int> vec = {1, 3, 5, 7, 9, 11, 13, 15};
    print_vec(vec, 0);
    cout << endl;

    system("pause");
    return 0;
}


练习 6.48

说明下面这个循环的含义,它对 assert 的使用合理吗?

string s;
while (cin >> s && s != sought) { } //空函数体
assert(cin);

解答

不合理。从这个程序的意图来看,应该用:

assert(s == sought);


练习 6.49

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

解答

  • 候选函数 (candidate function):与被调用函数同名,且其声明在调用点可见。
  • 可行函数 (viable function):形参与实参数量相等,且每个实参类型与对应形参类型相同或能转换成形参的类型。


练习 6.50

已知有第 217 页对函数 f 的声明,对于下面的每一个调用列出可行函数。 其中哪个函数是最佳匹配? 如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?

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

解答


练习 6.51

编写函数 f 的 4 版本,令其各输出一条可以区分的消息。验证上一个练习的答案,如果你的回答错了,反复研究本节内容直到你弄清自己错在何处。

解答

源程序:

#include <iostream>
using namespace std;

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)
{
    cout << "f(double, double)" << endl;
}

int main()
{   
    //f(2.56, 42);    // error: 'f' is ambiguous.
    f(42);
    f(42, 0);
    f(2.56, 3.14);

    system("pause");
    return 0;
}

输出:

f(int)
f(int, int)
f(double, double)

练习 6.52

已知有如下声明:

void manip(int, int);
double dobj;

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

(a) manip('a', 'z');
(b) manip(55.4, dobj);

解答

  • (a) 第 3 级。类型提升实现的匹配 (char 实参自动提升为 int)。
  • (b) 第 4 级。算术类型转换实现的匹配 (double 实参自动转换为 int)。

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

解答

(c) 不合法。顶层 const 不影响传入函数的对象。


练习 6.54

编写函数的声明,令其接受两个 int 形参并返回类型也是 int;然后声明一个 vector 对象,令其元素是指向该函数的指针。

解答

int func(int, int);
vector<decltype(func)*> ptr;  // 要点

练习 6.55

编写 4 个函数,分别对两个int值执行加、减、乘、除运算;在上一题创建的 vector 对象中保存指向这些函数的指针。

解答

#include <iostream>
#include <vector>
using namespace std;

int add(int a, int b) 
{ 
    return a + b; 
}

int sub(int a, int b) 
{ 
    return a - b; 
}

int mul(int a, int b) 
{ 
    return a * b; 
}

int div(int a, int b) 
{ 
    return b != 0 ? a / b : 0; 
}

int main()
{   
    decltype(add) *ptr1 = add, *ptr2 = sub, *ptr3 = mul, *ptr4 = div;
    vector<decltype(add)*> ptr = {ptr1, ptr2, ptr3, ptr4};

    system("pause");
    return 0;
}

练习 6.56

调用上述 vector 对象中的每个元素并输出结果

解答

源程序:

#include <iostream>
#include <vector>
using namespace std;

int add(int a, int b) 
{ 
    return a + b; 
}

int sub(int a, int b) 
{ 
    return a - b; 
}

int mul(int a, int b) 
{ 
    return a * b; 
}

int divi(int a, int b) 
{ 
    return b != 0 ? a / b : 0; 
}

int main()
{   
    decltype(add) *ptr1 = add, *ptr2 = sub, *ptr3 = mul, *ptr4 = divi;
    vector<decltype(add)*> func = {ptr1, ptr2, ptr3, ptr4};
    for (auto p : func) {
        cout << p(4, 2) << endl;
    }

    system("pause");
    return 0;
}

输出:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值