第九章

单独编译

头文件中常包含的内容:

  • 函数原型
  • 使用#defineconst定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

使用如下结构防止头文件被多次编译:

1
2
3
4
5
6
#ifndef COORDIN_H_
#define COORDIN_H_

// place include file content here

#endif // COORDIN_H_

不同的编译器,名称修饰可能不一样,所以不同编译器编译的二进制模块可能无法相互链接。

存储持续性、作用域和连接性

  • 自动存储持续性
  • 静态存储持续性
  • 线程存储持续性
  • 动态存储持续性

新的定义隐藏了(hide)以前的定义,新定义可见,旧定义暂时不可见。在程序离开该代码块时,原来的定义又重新可见。

自动变量存储在栈中。

C++11之后register关键字只是显式地指出变量时自动的。

静态存储持续性变量有三种链接性:

  • 外部链接性
  • 内部链接性
  • 无链接性

所有静态持续变量都有下述初始化特征:未被初始化的静态变量的所有位都被设置为0。这种变量被称为零初始化的(zero-initialized)。

由于单定义规则(One Deginition Rule, ODR)的存在,在引用的地方使用关键字extern

存储说明符:

  • auto
  • register
  • static
  • extern
  • thread_local
  • mutable

cv-限定符:

  • const
  • volatile

mutable用来指出即使结构(或类)变量为const,其某个成员也可以被修改。

常量类型可以放在头文件中,即使多个文件包含了该头文件,也不会出现重复定义错误,这是因为C++对这种情况做了处理。因为默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的。所以实际上每个文件都有自己的一套常量,而不是所有文件共享这套常量。

可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的。

包含头文件new可以使用定位new。使用方法例如p2 = new (buffer1) chaff;

定位new运算符的另一种用法是,将其与初始化结合使用,从而将信息放在特定的硬件地址处。

名称空间

用关键字namespace来创建名称空间。

名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。

全局名称空间(global namespace),对英语文件级声明区域,因此全局变量被描述为位于全局名称空间中。

未被装饰的名称称为未限定的名称(unqualified name);包含名称空间的名称称为限定的名称(qualified name)。

using声明将特定的名称添加到它所属的声明区域中。如:using Jill::fetch;使用using声明后,该声明区域中若再需要使用fetch,则不需要加Jill::。同时using声明也一样会隐藏同名的全局变量。
using编译使名称空间中的所有名称都可用,而不需要使用作用域解析运算符:using namespace Jack;

一般说来,使用using声明比使用using编译指令更安全,这是由于它只导入指定的名称。如果该名称与局部名称发生冲突,编译器将发出指示。using编译指令导入所有名称,包括可能并不需要的名称。如果与局部名称发生冲突,则局部名称将後盖名称空间版本,而编译器并不会发出警告。

通过省略名称空间的名称来创建未命名的名称空间,作用是控制名称的作用域。

名称空间使用的一些指导原则:

  • 使用在已命名的名称空间中声明的变量,而不是使用外部全局变量。
  • 使用在己命名的名称空间中声明的变量,而不是使用静态全局变量。
  • 如果开发了一个函数库或类库,将其放在一个名称空间中。事实上,C++当前提倡将标准函数库放在名称空间std中,这种做法扩展到了来自C语言中的函数。例如,头文件math.h是与C语言兼容的,没有使用名称空间,但C++头文件cmath应将各种数学库函数放在名称空间std中。实际上,并非所有的编译器都完成了这种过渡。
  • 仅将编译指令using作为一种将旧代码转换为使用名称空间的权宜之计。
  • 不要在头文件中使用using编译指令。首先,这样做掩盖了要让哪些名称可用;另外,包含头文件的顺序可能影响程序的行为。如果非要使用编译指令using,应将其放在所有预处理器编译指令#include之后。
  • 导入名称时,首选使用作用域解析运算符或using声明的方法。
  • 对于using声明,首选将其作用域设置为局部而不是全局。

编程练习

第九章编程练习

1

golf.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// golf.h -- for pe9-1.cpp

const int Len = 40;
struct golf
{
char fullname[Len];
int handicap;
};

// non-interactive version:
// function sets golf structure to provided name, handicap
// using values passed as arguments to the function
void setgolf(golf & g, const char * name, int hc);

// interactive version:
// function solicits name and handicap from user
// and sets the members of g to the values entered
// returns 1 if name is entered, 0 if name is empty string
int setgolf(golf & g);
// function resets handicap to new value
void handicap(golf & g, int hc);

// function displays contents of golf structure
void showgolf(const golf & g);

golf.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <string>
#include "golf.h"

void setgolf(golf & g, const char * name, int hc) {
strcpy(g.fullname, name);
g.handicap = hc;
}

int setgolf(golf & g) {
std::cout << "Enter a name: ";
std::cin.getline(g.fullname, Len);

if (strlen(g.fullname) == 0) return 0;

std::cout << "Enter handicap: ";
std::cin >> g.handicap;
std::cin.get();

return 1;
}

void handicap(golf & g, int hc) {
g.handicap = hc;
}

void showgolf(const golf & g) {
std::cout << "name: " << g.fullname << ", handicap: " << g.handicap << std::endl;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include "golf.h"

const int MAX_LEN = 5;

int main() {
golf v[MAX_LEN];
golf ann;
setgolf(ann, "Ann Birdfree", 24);

v[0] = ann;

int i;
for (i = 1; i < MAX_LEN && setgolf(v[i]); i++);

for (int j = 0; j < i; j++) {
std::cout << "original: ";
showgolf(v[j]);
handicap(v[j], j-1);
std::cout << "change handicap: ";
showgolf(v[j]);
}

return 0;
}

2

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <string>

using namespace std;

const int ArSize = 10;

void strcount(const string &s);

int main() {
string input;

cout << "Enter a line:" << endl;
getline(cin, input);
while (cin) {
strcount(input);
cout << "Enter next line (empty line to quit):" << endl;
getline(cin, input);
if (input == "") break;
}
cout << "Bye" << endl;

return 0;
}

void strcount(const string &s) {
static int total = 0;
cout << "\"" << s << "\" contains " << s.size() << " characters" << endl;
total += s.size();
cout << total << " characters total\n";
}

3

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <new>

using namespace std;

struct chaff {
char dross[20];
int slag;
};

char buffer[1024];

int main() {
chaff *pcha = new (buffer) chaff[2];
char *pc = new char[1024];
chaff *pcha2 = new (pc) chaff[2];
char dross[20] = { 0 };
int slag = 0;

for (int i = 0; i < 2; i++)
{
cout << "Enter dross of #" << i << " chaff: " << endl;
cin.getline(dross, 20);
cout << "Enter slag of #" << i << " chaff: " << endl;
cin >> slag;
cin.get();

strcpy(pcha[i].dross, dross);
strcpy(pcha2[i].dross, dross);
pcha[i].slag = pcha2[i].slag = slag;
}

for (int i = 0; i < 2; i++)
{
cout << "staff #" << (i + 1) << ":" << endl;
cout << "pcha.dross: " << pcha[i].dross << ". pcha.slag: " << pcha[i].slag << endl;
cout << "pcha2.dross: " << pcha2[i].dross << ". pcha2.slag: " << pcha2[i].slag << endl;
}

cout << "address of buffer: " << (void *)buffer << endl;
cout << "address of pcha: " << pcha << ". address of pcha[0]: " << &pcha[0] << ". address of pcha[1]: " << &pcha[1] << endl;
cout << "address of pc: " << (void *)pc << endl;
cout << "address of pcha2:" << pcha2 << ". address of pcha2[0]: " << &pcha2[0] << ". address of pcha2[1]: " << &pcha2[1] << endl;;

delete[] pc;

return 0;
}

4

sales.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//
// sales.hpp
// 9-4
//
// Created by 郭帆 on 2023/7/20.
//

#ifndef sales_hpp
#define sales_hpp

namespace SALES {

const int QUARTERS = 4;
struct Sales {
double sales[QUARTERS];
double average;
double max;
double min;
};

void setSales(Sales & s, const double ar[], int n);
void setSales(Sales & s);
void showSales(const Sales & s);

}

#endif /* sales_hpp */

sales.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//
// sales.cpp
// 9-4
//
// Created by 郭帆 on 2023/7/20.
//

#include <iostream>
#include "sales.hpp"

namespace SALES {

void setSales(Sales &s, const double ar[], int n)
{
double total = ar[0], maxv = ar[0], minv = ar[0];
for (int i = 1; i < n; i++)
{
s.sales[i] = ar[i];
total += ar[i];
maxv = ar[i] > maxv ? ar[i] : maxv;
minv = ar[i] < minv ? ar[i] : minv;
}
s.min = minv;
s.max = maxv;
s.average = total / n;
}

void setSales(Sales &s)
{
using namespace std;
int len;
cout << "Enter the length of sales(<= 4 and > 0): ";
while (!(cin >> len) || len > 4 || len <= 0)
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Please enter a number(<= 4 and > 0): ";
}
double *temp = new double[len];
cout << "Please enter the sales:" << endl;
for (int i = 0; i < len; i++)
{
cout << "Please enter the content #" << i + 1 << ": ";
while (!(cin >> temp[i]))
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Please enter a number: ";
}
}
setSales(s, temp, len);
delete[] temp;
}

void showSales(const Sales &s)
{
std::cout << "Sales average: " << s.average << std::endl;
std::cout << "Sales max: " << s.max << std::endl;
std::cout << "Sales min: " << s.min << std::endl;
}

}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include "sales.hpp"

int main() {
SALES::Sales objects[2];
const double temp[4] = {1.0, 2.0, 3.0, 4.0};

SALES::setSales(objects[0]);
SALES::setSales(objects[1], temp, 4);
SALES::showSales(objects[0]);
SALES::showSales(objects[1]);
std::cout << "Bye." << std::endl;

return 0;
}