一、复习题
1. 对于下面的情况,应使用哪种存储方案?
a. homer 是函数的形参。
b. secret 变量由两个文件共享。
c. topsecret 变量由一个文件中的所有函数共享,但对于其他文件来说是隐藏的。
d. beencalled 记录包含它的函数被调用的次数
答:
a. 自动存储持续性
b. 静态存储持续性(具有外部链接性)
c. 静态存储持续型(具有内部链接性)
d. 静态存储持续性(没有链接性)
2. using声明和using编译指令之间有何区别?
答:使用using声明只声明名称空间的一个名称,当有局部名称与名称空间的名称冲突时,编译器会报错。而使用using编译指令则会声明名称空间的所有名称,当有局部名称与名称空间的名称冲突时,编译器不会报错,而是隐藏名称空间中的名称。在局部范围(函数中)使用上述两种方法,声明的名称局部可用,在全局范围(文件/所有函数外)声明使用,声明的名称全局可用。
3. 重新编写下面的代码,使其不使用using声明和using编译指令。
#include <iostream>
using namespace std;
int main()
{
double x;
cout << "Enter value: ";
while (! (cin >> x) )
{
cout << "Bad input. Please enter a number: ";
cin.clear();
while (cin.get() != '\n')
continue;
}
cout << "Value = " << x << endl;
return 0;
}
答:不使用using声明和using编译指令,那就只能使用名称加上作用域解析运算符。
// 头文件
#include <iostream>
int main()
{
double x;
std::cout << "Enter value: ";
while (! (std::cin >> x) )
{
std::cout << "Bad input. Please enter a number: ";
std::cin.clear();
while (std::cin.get() != '\n')
continue;
}
std::cout << "Value = " << x << std::endl;
return 0;
}
4. 重新编写下面的代码,使之使用using声明,而不是using编译指令。
#include <iostream>
using namespace std;
int main()
{
double x;
cout << "Enter value: ";
while (! (cin >> x) )
{
cout << "Bad input. Please enter a number: ";
cin.clear();
while (cin.get() != '\n')
continue;
}
cout << "Value = " << x << endl;
return 0;
}
答:使用using编译指令需要把使用的名称一一列出。
// 头文件
#include <iostream>
int main()
{
// using声明
using std::cout;
using std::cin;
using std::endl;
double x;
cout << "Enter value: ";
while (! (cin >> x) )
{
cout << "Bad input. Please enter a number: ";
cin.clear();
while (cin.get() != '\n')
continue;
}
cout << "Value = " << x << endl;
return 0;
}
5. 在一个文件中调用average(3, 6)函数时,它返回两个int参数的int平均值,在同一个程序的另一个文件中调用时,它返回两个int参数的double平均值。应该如何实现?
答:首先,两个函数的参数列表的参数数量、类型和顺序均相同,只有返回值不同,所以不能使用函数重载。可以利用本章学习的关键字static修饰函数(函数声明和定义都需要),把函数的外部链接属性变成内部链接属性,函数只能在它所在的函数使用,这样两个函数就不会冲突。还可以创建两个名称空间,把两个函数分别放进去,使用哪一个就表明是哪一个名称空间的函数。
6. 下面的程序由两个文件组成,该程序显示什么内容?
// file1.cpp
#include <iostream>
using namespace std;
void other();
void another();
int x = 10;
int y;
int main()
{
cout << x << endl;
{
int x = 4;
cout << x << endl;
cout << y << endl;
}
other();
another();
return 0;
}
void other()
{
int y = 1;
cout << "Ohter: " << x << ", " << y << endl;
}
// file 2.cpp
#include <iostream>
using namespace std;
extern int x;
namespace
{
int y = -4;
}
void another()
{
cout << "another(): " << x << ", " << y << endl;
}
答:在file1.cpp文件中,在main()函数中先输出全局变量x,然后代码块中局部变量x隐藏了全局变量x,全局变量y没有初始化,编译器自动设置为0,所以输出的x和y为4和0。而other函数中的局部变量y隐藏了全局变量y,输出的x和y为10和1。最后的another()函数的定义在file2.cpp文件中,该文件声明了全局变量x来自其他文件(也就是file1.cppz中),而y使用的是为命名的名称空间中定义的变量y(相当于static int y = -4,静态存储持续型,内部链接性),所以输出的x和y为10和-4。
程序运行结果如下:
7. 下面的代码将显示什么内容?
#include <iostream>
using namespace std;
void other();
namespace n1
{
int x = 1;
}
namespace n2
{
int x = 2;
}
int main()
{
using namespace n1;
cout << x << endl;
{
int x = 4;
cout << x << ", " << n1::x << ", " << n2::x << endl;
}
using n2::x;
cout << x << endl;
other();
return 0;
}
void other()
{
using namespace n2;
cout << x << endl;
{
int x = 4;
cout << x << ", " << n1::x << ", " << n2::x << endl;
}
using n2::x;
cout << x << endl;
}
答:在main()函数中,using编译指令声明了n2名称空间的所有内容,先输出x的值为1,然后代码块中的局部变量隐藏了n2空间中的x,显示x的值为4,然后显示指出x的来源名称空间,显示两个x的值为1和2,出代码块之后,using声明了n2名称空间中的x,隐藏了n1名称空间中的x,显示x的值为2。最后进入other()函数,和main()函数中的情况类似,这里就不再赘述。
程序运行结果如下:
二、编程练习
1. 下面是一个头文件:
// golf.h-forgr9-1.cpp
const int Len = 40;
struct golf
{
char fullname[Len];
int handicap;
};
// 使用非交互版本:
// 使用传递给函数的参数值,函数为提供的名称和handicap设置golf结构体
void setgolf(golf& g, const char* name, int hc);
// 交互式版本
// 函数申请用户的名称和handicap
// 设置输入的g的个数
// 如果输入名称,返回1;如果名称是空字符串,返回0
int setgolf(golf& g);
// 函数重置handicap为新的值
void handicap(golf& g, int hc);
// 函数显示golf结构体的信息
void showgolf(const golf& g);
注意,setgolf()被重载,可以这样使用其第一个版本:
golf ann;
setgolf(andy);
答:
test.cpp
// 头文件
#include "golf.h"
// using声明
using std::cout;
using std::endl;
using std::cin;
// 符号常量声明
const int size = 5;
int main()
{
// 非交互版本测试
golf g1;
setgolf(g1, "原神", 888);
showgolf(g1);
cout << endl;
// 交互版本测试
golf golfs[size];
int i = 0;
do
{
// 正确输入则显示
if (setgolf(golfs[i]))
{
cin.get(); // 丢弃输入handicap遗留的换行符,也可以直接在setgolf()函数中丢弃
showgolf(golfs[i]);
}
else
{
break;
}
} while (1); // 输入姓名不为空行
return 0;
}
golf.cpp
#define _CRT_SECURE_NO_WARNINGS
// 头文件
#include "golf.h"
// using声明
using std::cin;
using std::cout;
using std::endl;
// 函数定义
// 使用非交互版本:
// 使用传递给函数的参数值,函数为提供的名称和handicap设置golf结构体
void setgolf(golf& g, const char* name, int hc)
{
strcpy(g.fullname, name);
g.handicap = hc;
}
// 交互式版本
// 函数申请用户的名称和handicap
// 设置输入的g的个数
// 如果输入名称,返回1;如果名称是空字符串,返回0
int setgolf(golf& g)
{
// 输入名称
char name[Len];
cout << "Please enter the name: ";
cin.getline(name, Len);
// 如果输入为空行
if (name[0] == '\0')
{
cout << "Enter blank line." << endl;
// 重置输入且清空无效输入
cin.clear();
while (cin.get() != '\n')
continue;
return 0;
}
// 正确输入名称
strcpy(g.fullname, name);
static int num_golf = 0; // 输入g的个数
++num_golf;
// 输入handicap
cout << "Please enter the handicap: ";
while (!(cin >> g.handicap))
{
// 重置输入
cin.clear();
// 清除错误输入
while (cin.get() != '\n') continue;
// 重新输入
cout << "Please enter the handicap: ";
}
return 1;
}
// 函数重置handicap为新的值
void handicap(golf& g, int hc)
{
g.handicap = hc;
}
// 函数显示golf结构体的信息
void showgolf(const golf& g)
{
cout << "Name: " << g.fullname << endl;
cout << "Handicap: " << g.handicap << endl;
}
2. 修改程序清单9.9,要求用string对象代替字符数组。这样,该程序将不再需要判断输入的字符串是否过长,同时可以将输入字符串同字符串""进行比较,以判断输入内容是否为空行。
程序清单9.9如下:
// static.cpp —— 使用静态的局部变量
#include <iostream>
// 常量
const int ArSize = 10;
// 函数原型
void strcount(const char* str);
int main()
{
using namespace std;
char input[ArSize];
char next;
cout << "Enter a line:\n";
cin.get(input, ArSize);
while (cin)
{
cin.get(next);
while (next != '\n')
cin.get(next);
strcount(input);
cout << "Enter next line (empty line to quit):\n";
cin.get(input, ArSize);
}
cout << "Bye\n";
return 0;
}
void strcount(const char* str)
{
using namespace std;
static int total = 0;
int count = 0;
cout << "\"" << str << "\" contains ";
while (*str++)
++count;
total += count;
cout << count << "characters\n";
cout << total << "characters total\n";
}
答:
// 头文件
#include <iostream>
#include <string>
// using声明
using std::cout;
using std::cin;
using std::endl;
using std::string;
// 函数声明
void strcount(const string& str);
int main()
{
// 输入
string str;
cout << "Enter a line:\n";
getline(cin, str);
while (str != "")
{
strcount(str);
cout << "Enter next line (empty line to quit):\n";
getline(cin, str);
}
cout << "Bye.\n";
return 0;
}
// 函数定义
void strcount(const string& str)
{
static int total = 0; // 记录输入字符串的总字符数
int count = (int)str.size(); // 当前字符串的字符数
cout << "\"" << str << "\" contains ";
total += count;
cout << count << "characters\n";
cout << total << "characters total\n";
}
3. 下面是一个结构体的声明。
// chaff结构声明
struct chaff
{
char dross[20];
int slag;
};
编写一个程序,使用定位运算符new将一个包含两个这种结构体的数组放在一个缓冲区中。然后为结构体成员赋值(对于char数组使用strcpy()),并使用一个循环来显示内容。一种方法是像程序清单9.10那样以一个静态数组作为缓冲区;另一种方法是使用常规new运算符来分配空间。
答:
// 头文件
#include <iostream>
#include <new> // 定位运算符new
// using声明
using std::cin;
using std::cout;
using std::endl;
// 符号常量声明
const int SIZE = 512;
// staff结构声明
struct staff
{
char dross[20];
int slag;
};
// 函数声明
void set_staff(staff& s); // 设置staff
void show_staff(const staff& s); // 显示staff
int main()
{
// 申请空间
char* buff = new char[SIZE];
// 定位空间
staff* ps = new(buff) staff[2]; // 定位到buff这块空间的首地址
// 输出检验是否定位成功
cout << "buff: " << (int*)buff << endl; // 不强制类型转换会被当作字符串输出
cout << "ps : " << ps << endl;
// 为结构成员赋值
for (int i = 0; i < 2; ++i)
{
set_staff(ps[i]);
}
// 显示
for (int i = 0; i < 2; ++i)
{
show_staff(ps[i]);
}
// 释放申请空间
free(buff);
return 0;
}
// 函数定义
void set_staff(staff& s) // 设置staff
{
cout << "Enter the dross: ";
cin.getline(s.dross, 20);
// 下面这个while循环有小瑕疵,但是运行没问题,只供参考
while (!strcmp(s.dross, ""))
{
cin.clear();
while (cin.get() != '\n') continue;
cout << "Error input! Enter the dross: ";
cin.getline(s.dross, 20);
}
cout << "Enter the slag: ";
while (!(cin >> s.slag))
{
cin.clear();
while (cin.get() != '\n') continue;
cout << "Error intput! Enter the slag: ";
}
cin.get(); // 丢弃换行符
}
void show_staff(const staff& s) // 显示staff
{
cout << endl;
cout << "Dross: " << s.dross << endl;
cout << "Slag: " << s.slag << endl;
}
4. 请基于下面这个名称空间编写一个由3个文件组成的程序。
// 头文件
#include <iostream>
// 名称空间
namespace SALES
{
const int QUARTERS = 4;
struct Sales
{
double sales[QUARTERS];
double average;
double max;
double min;
};
// 把数组ar中的项(最多4项)复制到s
// 的sales成员中,计算s,存储转入的项
// 的平均值,最大值和最小值
// 如果sales有其他元素,把它们设置为0
void setSales(Sales& s, const double ar[], int n);
// 收集4个季度的销量,在s的sales成员中存储它们,并计算均值、最大值和最小值
void setSales(Sales& s);
// 显示结构体的所有信息
void showSales(const Sales& s);
}
答:
main()函数文件:
// 头文件
#include "SALES.h"
// using 编译指令
using namespace SALES;
using std::cout;
using std::endl;
int main()
{
// 交互式版本
Sales s1;
setSales(s1);
showSales(s1);
cout << endl;
// 非交互版本
Sales s2;
double sales[3] = { 10, 20, 30 };
setSales(s2, sales, 3);
showSales(s2);
return 0;
}
SALES文件:
// 头文件
#include "SALES.h"
// using 编译指令
using namespace SALES;
// using 声明
using std::cout;
using std::cin;
using std::endl;
// 把数组ar中的项(最多4项)复制到s
// 的sales成员中,计算s,存储转入的项
// 的平均值,最大值和最小值
// 如果sales有其他元素,把它们设置为0
void SALES::setSales(Sales& s, const double ar[], int n)
{
double sum = 0;
double max = ar[0];
double min = ar[0];
// 设置sales值,找出最大值、最小值、计算总值
for (int i = 0; i < 4; ++i)
{
if (i < n)
s.sales[i] = ar[i];
else
s.sales[i] = 0;
if (s.sales[i] > max)
max = s.sales[i];
if (s.sales[i] < min)
min = s.sales[i];
sum += s.sales[i];
}
s.max = max;
s.min = min;
s.average = sum / 4;
}
// 收集4个季度的销量,在s的sales成员中存储它们,并计算均值、最大值和最小值
void SALES::setSales(Sales& s)
{
cout << "Please enter 4 sales: ";
for (int i = 0; i < 4; ++i)
{
while (!(cin >> s.sales[i]))
{
cin.clear();
while (cin.get() != '\n') continue;
cout << "Error input! Please re-enter:";
}
}
double max = s.sales[0];
double min = s.sales[0];
double sum = s.sales[0];
for (int i = 1; i < 4; ++i)
{
if (s.sales[i] > max)
max = s.sales[i];
if (s.sales[i] < min)
min = s.sales[i];
sum += s.sales[i];
}
s.max = max;
s.min = min;
s.average = sum / 4;
}
// 显示结构体的所有信息
void SALES::showSales(const Sales& s)
{
cout << "sales:\n";
for (int i = 0; i < 4; ++i)
{
cout << "No." << i +1 << ": " << s.sales[i] << endl;
}
cout << "max: " << s.max << endl;
cout << "min: " << s.min << endl;
cout << "average: " << s.average << endl;
}