练习 6.11 编写并验证你自己的reset
函数,使其作用于引用类型的参数。
#include<iostream>
using namespace std;
void reset(int &a) {
a = 0;
}
int main()
{
int x = 10;
cout << "x: " << x << endl;
reset(x);
cout << "x: " << x << endl;
return 0;
}
练习 6.12 改写6.2.1节练习中的程序,使其引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?
#include<iostream>
using namespace std;
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
return;
}
int main()
{
int a = 1, b = 2;
cout << "a: " << a << "\nb: " << b << endl;
swap(a, b);
cout << "a: " << a << "\nb: " << b << endl;
return 0;
}
显然引用更好。
练习 6.13 假设 T 是某种类型的名字,说明以下两个函数声明的区别:一个是void f(T)
, 另一个是 void f(&T)
。
前者是传值,不能通过形参改变实参。
后者是传引用,可以通过形参改变实参。
ps:我觉得后面的那种写法应该是 void f(T &)
,直接用树上的写法会编译出错。
练习 6.14 举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。
交换两个变量的数值,形参需要用引用类型。
当不希望函数改变实参的值而函数中又会更改形参的值时,则形参不能用引用类型。
练习 6.15 说明find_char
函数中的三个形参为什么是现在的类型,特别说明为什么s是常量引用而occurs
是普通引用?为什么s
和occurs
是引用类型而c不是?如果令s是普通引用会发生什么情况?如果令occurs
是常量引用会发生什么情况?
- 使用传值方式的话,字符串复制的开销很大,所以传入引用;因为函数保证不会修改s的值,所以用const引用来加以限制。
- 因为调用者是通过occurs来隐式地获得c的出现次数的,所以需要函数在其操作中修改occurs来进行计数并返回给调用者。
- 因为c通常情况下会传入一个字面值,并且由于c仅仅是一个char类型,所以通过传值方式调用更方便快捷。
- 在这个函数中不会发生特殊情况,但是如果不加const限制,则有可能会在函数中修改s变量。
- 会导致函数不能正常修改occurs,导致错误。
练习 6.16 下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。
bool is_empty(string& s) { return s.empty(); }
局限性在于常量字符串和字符串字面值无法作为它的实参
可以改为:
bool is_empty(const tring &s){return s.empty();}
练习 6.17 编写一个函数,判断string
对象中是否含有大写字母。编写另一个函数,把string
对象全部改写成小写形式。在这两个函数中你使用的形参类型相同吗?为什么?
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
bool haveupper(const string &s) {
bool flag = false;
for (auto c : s) {
if (isupper(c)) {
flag = true;
break;
}
}
return flag;
}
void beclower(string &s) {
bool flag = false;
for (auto &c : s) {
c = tolower(c);
}
}
int main()
{
string a = "ABCDaaaa";
cout << a << endl;
cout << haveupper(a) << endl;
beclower(a);
cout << a << endl;
cout << haveupper(a) << endl;
return 0;
}
不同,因为函数的需求不同,第一个函数只需要访问字符串s即可,所以用const
引用类型,而第二个函数需要访问并修改字符串s,所以用普通引用类型。
练习 6.18 为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。
- (a) 名为
compare
的函数,返回布尔值,两个参数都是 matrix 类的引用。 - (b) 名为
change_val
的函数,返回vector
的迭代器,有两个参数:一个是int
,另一个是vector
的迭代器。
bool compare(matrix &a, matrix &b);
vector<int>::iterator change_val(int x, vector<int>::iterator iter)
练习 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)不合法,calc只有一个参数,调用时却传入了两个值。
(b)合法
©合法
(d)合法
练习 6.20 引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况?
当只需要访问实参的值,而不需要更改实参时,一个用常量引用。
可能会导致本应该合法的调用不合法。