C++ 程序设计基础

本文最后更新于 2025年8月14日 星期四 16:49

前言

周霭如, 林伟健, 徐红云. C++ 程序设计基础第 6 版. 电子工业出版社, 2021. 2022 年 5 月第 3 次印刷. 978-7-121-41275-2.

其中部分表述可能与原书有出入,代码皆在 C++ 17 标准下编译通过。

下载习题与解答

第 1 章 简单程序与基本数据类型

1.4 数据对象与访问

1.4.2 访问变量

1.4.2.3 指针变量与间址访问

定义指针的格式:类型* 标识符;

  • 【例 1-7】用指针变量访问所指对象。P14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <bits\stdc++.h>
using namespace std;

int main() {
// p1,p2存放a,b的地址
int a(10), b(20), t;
int *p1(&a), *p2(&b), *pt; // 用地址初始化指针变量
cout << p1 << "\t" << p2 << endl; // 地址
printf("%#04X\t%#04X\n", p1, p2); // 地址(笔者附加)
printf("%d\t%d\n", *p1, *p2); // 值

t = *p1;
*p1 = *p2;
*p2 = t; // 交换值

pt = p1;
p1 = p2;
p2 = pt; // 交换指针变量的值(地址)
return 0;
}
1.4.2.4 void 型指针(空类型)
  • 【例 1-8】void 型指针的强制类型转换。P15
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <bits\stdc++.h>
using namespace std;

int main() {
int a = 65;
int* ip;
void* vp = &a;
cout << *(int*)vp << endl; // 65
cout << *(char*)vp << endl; // A
ip = (int*)vp;
cout << *(ip) << endl; // 65
return 0;
}
1.4.2.5 引用(对象别名)

引用说明的格式:类型& 引用名 = 对象名;

  • 【例 1-9】引用测试。P16
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <bits\stdc++.h>
using namespace std;

int main() {
int a = 2345;
int* pa;
int& ra = a;
pa = &a;
printf("%d\t%d\t%d\n", a, ra, *pa); // a的值
cout << &a << " " << &ra << " " << pa << endl; // a的地址
cout << &pa << endl; // 指针pa的地址
return 0;
}

1.4.3 常量与约束访问

1.4.3.2 指向常量的指针

定义常量的格式:const 类型 常量标识符 = 常量表达式;

指向常量的指针:用 const 约束指针对所指对象的访问。

定义指向常量的指针的格式 1:const 类型* 指针; 定义指向常量的指针的格式 2:类型 const *指针;

特性

  1. 间址只读
  2. 可改指向
  • P17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits\stdc++.h>
using namespace std;

int main() {
int a = 35;
const int M = 1000;
int* p;
const int* P1;
const int* P2;

// 指向常量的指针可以获取变量或常量的地址,但限制了用指针间址访问对象方式为只读
P1 = &a;
P2 = &M;
//*P1 = 100; //错误,不能修改指向常量的指针
//*P2 = 200; //错误,不能修改指向常量的指针
a = *P1 + *P2;
// p = &M; //错误,常量地址不能赋给普通指针,常量地址只能赋给指向常量的指针
return 0;
}
1.4.3.3 指针常量

定义指针常量的格式:类型* const 指针;

特性

  1. 间址可读可写
  2. 不可改指向

const 写在指针之前,表示约束指针变量本身

  • P18
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <bits\stdc++.h>
using namespace std;

int main() {
int a = 100, b = 200;
int* const P1 = &a;
// P1 = &b; //错误,不能修改指针常量
*P1 = b;

const int M = 1000;
// int* const P2 = &M; //错误,不能用无约束间址访问的指针获取标识常量的地址
return 0;
}
1.4.3.4 指向常量的指针常量

定义指向常量的指针常量的格式 1:const 类型* const 指针; 定义指向常量的指针常量的格式 2:类型 const *const 指针;

特性

  1. 间址只读
  2. 不可改指向
  • P18
1
2
3
4
5
6
7
8
9
10
11
12
#include <bits\stdc++.h>
using namespace std;

int main() {
int a = 128, b = 256;
const int M = 1000;
const int* const P1 = &a;
const int* const P2 = &M;
// P1 = &b; //错误,不能写指针常量
//*P2 = 500; //错误,不能写指向常量的指针常量
return 0;
}
1.4.3.5 常引用

定义常引用的格式:const 类型& 引用名 = 对象名;

  • P18
1
2
3
4
5
6
7
8
9
10
11
#include <bits\stdc++.h>
using namespace std;

int main() {
int a = 863;
const int& ra = a; // ra是a的常引用(别名)
// ra = 985; //错误,不能通过常引用对对象a执行写操作
a = 985;
// 通过ra对a操作,只能读,不能写
return 0;
}

第 2 章 程序控制结构

2.1 表达式

短路计算

  1. V = E1 && E2

    • 遇到假则退出,全真取最后一个表达式
  2. V = E1 || E2

    • 遇到真则退出,全假取最后一个表达式

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

int a, b;

int main() {
a = b = 0;
cout << (a++ && b++) << ", " << a << " " << b << endl; // 0, 1 0
a = b = 1;
cout << (a++ && b++) << ", " << a << " " << b << endl; // 1, 2 2
a = b = 0;
cout << (a++ || b++) << ", " << a << " " << b << endl; // 0, 1 1
a = b = 1;
cout << (a++ || b++) << ", " << a << " " << b << endl; // 1, 2 1
return 0;
}

第 3 章 函数

3.2 函数参数的传递

3.2.2 指针参数

这种参数传递方式称为指针传递或地址调用。

  • 【例 3-9】通过函数及其指针参数来实现 2 个整型变量的值交换。P63
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

void swap(int* x, int* y) {
int temp = *x;
*x = *y;
*y = temp;
}

int main() {
int a = 3, b = 8;
printf("%d %d\n", a, b);
swap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}
  • 【例 3-10】使用 const 限定指针,保护实参对象。P63
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

int func(const int* const p) {
int a = 10;
a += *p;
//*p = a; //错误,不能修改const对象
// p = &a; //错误。若要执行,可以不约束p的访问:const int* p
return a;
}

int main() {
int x = 10;
printf("%d\n", func(&x)); //输出20
return 0;
}

3.2.3 引用参数

  • 【例 3-12】通过函数及其引用参数来实现 2 个整型变量的值交换。P64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

void swap(int& x, int& y) {
int temp = *x;
*x = *y;
*y = temp;
}

int main() {
int a = 3, b = 8;
printf("%d %d\n", a, b);
swap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}
  • 【例 3-13】使用 const 引用参数。P65
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits\stdc++.h>
using namespace std;

void display(const int& rk) {
cout << rk << ":\ndec: " << dec << rk << "\noct: " << oct << rk
<< "\nhex: " << hex << rk << endl;
}

int main() {
int m = 2618;
display(m);
display(4589);
return 0;
}

3.2.4 函数的返回类型

3.2.1.2 返回指针类型
  • 【例 3-15】定义 1 个函数,返回较大值变量的指针。P67
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

int* maxPoint(int* x, int* y) {
if (*x > *y) {
return x;
}
return y;
}

int main() {
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", *maxPoint(&a, &b));
return 0;
}

为了约束对实参的访问,可以改写为:

1
const int* maxPoint(const int* x, const int* y) { ... }
3.2.1.3 返回引用类型
  • 【例 3-16】定义 1 个函数,返回较大值变量的引用。P68
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

int& maxRef(int& x, int& y) {
if (x > y) {
return x;
}
return y;
}

int main() {
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", maxRef(a, b));
return 0;
}
  • 【例 3-17】统计正负整数个数,以 0 结束。P68
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits\stdc++.h>
using namespace std;

int a, b;

int& count(int n) {
if (n > 0) {
return a;
}
return b;
}

int main() {
int x;
scanf("%d", &x);
while (x) {
++count(x);
scanf("%d", &x);
}
printf("positive: %d\nnegative: %d\n", a, b);
return 0;
}
  • 【例 3-22】汉诺塔问题。P74
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits\stdc++.h>
using namespace std;

void move(int n, char a, char b, char c) {
if (n == 1) {
printf("%c-->%c\n", a, c); // 只有一个金片
} else {
move(n - 1, a, c, b); // 把n-1个金片从a移到b,以c为过渡
printf("%c-->%c\n", a, c); // 从a移一个金片到c
move(n - 1, b, a, c); // 把n-1个金片从b移到c,以a为过渡
}
}

int main() {
int m;
scanf("%d", &m);
move(m, 'A', 'B', 'C');
return 0;
}

3.4 函数地址与函数指针

3.4.1 函数地址

  • 【例 3-24】用不同方式调用函数。P75
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits\stdc++.h>
using namespace std;

void simple() { cout << "It is a simple programme.\n"; }

int main() {
simple();
(&simple)(); // 地址方式调用
(*&simple)(); // 间址调用
cout << simple << endl;
cout << &simple << endl;
cout << *&simple << endl; // 三者都是函数在内存中的入口地址,称为函数地址
return 0;
}

3.4.2 函数指针

  • 【例 3-25】用函数指针调用不同函数。P78
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <bits\stdc++.h>
using namespace std;

const double PI = 3.14159;

double circlePerimeter(double radius) { return 2 * PI * radius; }

int main() {
double (*pf)(double); // 定义函数指针
double r = 3.5, cP;
pf = circlePerimeter; // 获取函数地址
cP = pf(r); // 等价于circlePerimeter(r)
cout << "The perimeter of the circle is: " << cP << endl;
return 0;
}
  • 【例 3-26】】使用函数指针参数调用函数。P79
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits\stdc++.h>
using namespace std;
typedef double funType(double); // 定义函数类型

const double PI = 3.14159;

funType circlePerimeter; // 用函数类型名定义函数原型

double callFun(funType *qf, double r) {
// 第一个参数是函数指针参数
return qf(r);
}

double circlePerimeter(double radius) { return PI * radius * radius; }

int main() {
double r = 3.5;
// 用函数地址作为实参调用函数callFun
cout << "The perimeter of the circle is: " << callFun(circlePerimeter, r)
<< endl;
return 0;
}

第 4 章 数组

4.1 一维数组

4.1.1 一维数组的定义与初始化

表达式 sizeof(c)/sizeof(int) 用于计算数组元素个数。

4.1.2 一维数组的访问

4.1.2.2 以指针方式访问数组
  • 【例 4-3】用不同方式访问数组。P103
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
#include <bits\stdc++.h>
using namespace std;

int main() {
int a[] = {1, 3, 5, 7, 9}, *p;
for (int i = 0; i < 5; ++i) {
printf("a[%d]=%d\t", i, a[i]); // 下标方式访问
}
puts("");
p = a;
for (int i = 0; i < 5; ++i) {
printf("a[%d]=%d\t", i, p[i]); // 指针作为下标访问
}
puts("");
for (int i = 0; i < 5; ++i) {
printf("a[%d]=%d\t", i, *(a + i)); // 指针方式访问
}
puts("");
for (p = a; p < a + 5; ++p) {
printf("a[%d]=%d\t", p - a, *p); // 指针间址方式访问1
}
puts("");
for (p = a; p < a + 5;) {
printf("a[%d]=%d\t", p - a, *(p++)); // 指针间址方式访问2,或其中为*p++
}
puts("");
return 0;
}

4.2 指针数组

4.2.2 指向数组的指针数组

  • 【例 4-5】指针数组管理几个类型相同的数组。P105
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits\stdc++.h>
using namespace std;

int main() {
double aa[2] = {1.1, 2.2}, bb[2] = {3.3, 4.4}, cc[2] = {5.5, 6.6}, *pf[3];
pf[0] = aa;
pf[1] = bb;
pf[2] = cc;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 2; ++j) {
printf("%lf ", *(pf[i] + j));
}
puts("");
}
return 0;
}

4.3 二维数组

4.3.2 二维数组的访问

4.3.2.2 以指针方式访问二维数组
  • 【例 4-8】测试数组地址。P110
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits\stdc++.h>
using namespace std;

int main() {
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int* p;
p = a[0];
for (int i = 1; p < a[0] + 12; ++p, ++i) {
cout << p << " ";
if (i % 4 == 0) {
puts("");
}
}
for (int i = 0; i < 3; ++i) {
cout << a[i] << " ";
}
puts("");
return 0;
}
  • 【例 4-9】用指向数组的指针访问二维数组。P110
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <bits\stdc++.h>
using namespace std;

int main() {
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int total;
int *p, (*pary)[4]; // pary是指向一位数组的指针
for (p = a[0]; p < a[0] + 12; ++p) { // 以一位数组形式访问二维数组
total += *p;
}
printf("total = %d\n", total);
for (int i = 0; i < 3; ++i) {
pary = a + i;
for (int j = 0; j < 4; ++j) {
printf("%d\t", *(*pary + j)); // 以指向数组的指针访问二维数组
}
puts("");
}
return 0;
}

4.4 数组作为函数参数

4.4.2 数组名作为函数参数

  • 【例 4-13】修改形参数组指针。P113
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 <bits\stdc++.h>
using namespace std;

void func(int x[], int num) {
for (int i = 0; i < num; ++i) {
++(*x);
++x; // 1 2 3变成2 3 4
}
printf("two:");
x -= num; // 指针返回起始位置
for (int i = 0; i < num; ++i) {
printf("\t%d", *x++); // 移动形参指针x
}
puts("");
return;
}

int main() {
int a[] = {1, 2, 3};
printf("one:");
for (int i = 0; i < 3; ++i) {
printf("\t%d", a[i]);
}
puts("");
func(a, sizeof(a) / sizeof(int));
printf("three:");
for (int i = 0; i < 3; ++i) {
printf("\t%d", a[i]);
}
puts("");
return 0;
}
  • 【例 4-14】数组的降维处理。P114
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <bits\stdc++.h>
using namespace std;

const int M = 4, N = 3;

int sum(int* pa, int col, int i, int j) { // pa是一级指针
int t = 0, *p;
for (p = pa + col * i; p < pa + col * (j + 1); ++p) { // 计算地址偏移
t += *p;
}
return t;
}

int main() {
int a[M][N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
printf("total row 0-3 : %d\n", sum(a[0], 3, 0, 3));
return 0;
}

4.5 动态存储

4.5.1 new 与 delete 操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits\stdc++.h>
using namespace std;

int main() {
int* p1 = new int(1);
int* p2 = new int[105];
*p1 = 2;
p2[0] = 3;
cout << *p1 << endl;
cout << p2[0] << endl;
delete p1;
delete[] p2;
return 0;
}

4.5.2 动态存储的应用

  • 【例 4-18】动态分配和释放存储空间。P118
  • 【例 4-19】用 new 申请基本类型空间时,可以用括号“()”对存储单元赋初值。P119
1
2
3
4
5
6
7
8
9
10
11
12
#include <bits\stdc++.h>
using namespace std;

int main() {
int* p = NULL;
p = new int;
p = new int(1);
*p = 20;
delete p;
p = NULL;
return 0;
}
  • 【例 4-20】编写函数,申请动态数组。P119
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
#include <bits\stdc++.h>
using namespace std;

void app(int*& pa, int len) { // pa是指针引用参数
pa = new int[len];
for (int i = 0; i < len; ++i) {
pa[i] = 0;
}
}

int main() {
int *ary = NULL, *t;
int n(5);
app(ary, n);
for (t = ary; t < ary + n; ++t) {
printf("%d ", *t); // 输出n个0
}
puts("");
for (int i = 0; i < n; ++i) {
ary[i] = 10 + i;
}
for (int i = 0; i < n; ++i) {
printf("%d ", ary[i]);
}
puts("");
delete[] ary;
ary = NULL;
return 0;
}

4.7 字符串

4.7.1 C 字符串

4.7.1.2 字符串的访问
  • 【例 4-25】测试字符输出。P124
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <bits\stdc++.h>
using namespace std;

int main() {
char str[] = "Hello world!";
char* s = "Hello world!"; // 会warning,字符型指针,实际上是一个内置字符数组
char* str2;
str2 = new char[20];
for (int i = 0; i < 6; ++i) {
cout << s[i];
}
for (int i = 6; i < 12; ++i) {
cout << *(s + i);
}
puts("");
return 0;
}

输入字符串:

1
gets_s(s, 10);  //输入字符串长度小于10

第 5 章 集合与结构

5.3 结构

5.3.2 访问结构

  • 【例 5-9】用指针访问结构。P148
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <bits\stdc++.h>
using namespace std;

struct Person {
char name[20];
int id;
double salary;
};

int main() {
Person pr1;
Person* pp;
pp = &pr1;
strcpy_s(pp->name, "Zhang Hua");
pp->id = 987654321;
pp->salary = 335.0;
cout << pp->name << "\t" << pp->id << "\t" << pp->salary << endl;
// 等价于(*pp).name
return 0;
}

第 6 章 类与对象

6.1 类与对象的定义和访问

6.1.2 访问对象成员

  • 【例 6-2】用指针访问对象成员。P168
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits\stdc++.h>
using namespace std;

class Tclass {
public:
int x, y;
void print() { cout << x << "," << y << endl; };
};

int add(Tclass* ptf) { return (ptf->x + ptf->y); }

int main() {
Tclass test, *pt = &test;
pt->x = 100;
pt->y = 200;
pt->print();
test.x = 150;
test.y = 450;
test.print();
cout << "x + y = " << add(&test) << endl; // 把对象地址传给指针参数
return 0;
}

6.2 构造函数与析构函数

6.2.2 带参数的构造函数

  • 【例 6-4】带参数的构造函数。P171
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
#include <bits\stdc++.h>
using namespace std;

class Date {
public:
Date(int y, int m, int d) { // 构造函数
year = y;
month = m;
day = d;
cout << year << "/" << month << "/" << day
<< ": Date object initialized.\n";
}
~Date() { // 析构函数
cout << year << "/" << month << "/" << day << ": Date object destroyed.\n";
}
void SetDate(int y, int m, int d) {
year = y;
month = m;
day = d;
}
void IsLeapYear() const { // 常成员函数
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
cout << "Leap year.\n";
} else {
cout << "Not leap year.\n";
}
}
void PrintDate() const { cout << year << "/" << month << "/" << day << endl; }

private:
int year, month, day;
};

int main() {
Date d1(2019, 5, 1);
d1.SetDate(1998, 6, 15);
d1.PrintDate();
d1.IsLeapYear();
return 0;
}
1
2
3
4
5
6
7
int main() {
Date* pd;
pd = new Date(1982, 6, 6);
pd->PrintDate();
delete (pd);
return 0;
}

6.2.4 拷贝构造函数

6.2.4.1 调用拷贝构造函数的时机
  • 【例 6-6】用已有对象初始化新创建的对象。P173
  • 【例 6-7】使用类类型传值参数的函数。P174
  • 【例 6-8】返回类类型的函数。P175
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
#include <bits\stdc++.h>
using namespace std;

class Location {
public:
Location(int xx = 0, int yy = 0) {
X = xx;
Y = yy;
cout << "Object constructed.\n";
}
Location(const Location& p) { // 拷贝构造函数
X = p.X; // 数据复制
Y = p.Y;
cout << "Copy_constructor called.\n";
}
~Location() { cout << X << ", " << Y << " Object destroyed.\n"; }
int GetX() const { return X; }
int GetY() const { return Y; }

private:
int X, Y;
};

Location g() { // g函数返回Location类型
Location A(1, 2);
return A;
}

int main() {
Location A(1, 2);
Location B(A); // 说明对象B,用A作为初值,调用拷贝构造函数
Location C;
C = g();
return 0;
}
6.2.4.2 浅复制和深复制
  • 【例 6-10】使用自定义的拷贝构造函数。P177
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 <bits\stdc++.h>
using namespace std;

class Name {
public:
Name(char *pn) {
cout << "Constructing " << pn << endl;
pName = new char[strlen(pn) + 1];
if (pName != 0) strcpy_s(pName, strlen(pn) + 1, pn);
size = strlen(pn);
}
Name(const Name &Obj) {
// 定义拷贝构造函数,实现“深复制”,否则会有“释放空指针”的错误(“浅复制”)
cout << "Copying " << Obj.pName << " into its own block.\n";
pName = new char[strlen(Obj.pName) + 1];
if (pName != 0) strcpy_s(pName, strlen(Obj.pName) + 1, Obj.pName);
size = Obj.size;
}
~Name() {
cout << "Destructing " << pName << endl;
pName[0] = '\0';
delete[] pName;
pName = NULL;
size = 0;
}
void setName(char *pn) {
delete[] pName;
pName = new char[strlen(pn) + 1];
if (pName != 0) strcpy_s(pName, strlen(pn) + 1, pn);
size = strlen(pn);
}
void showName() { cout << pName << endl; }

protected:
char *pName;
int size;
};

int main() {
Name Obj1("NoName");
Name Obj2 = Obj1; // 调用拷贝构造函数
Obj1.showName();
Obj2.showName();
Obj1.setName("SuDongpo"); // 对Obj1置值
Obj2.setName("DuFu"); // 对Obj2置值
Obj1.showName();
Obj2.showName();
return 0;
}

6.3 类的其他成员

6.3.1 常成员

6.3.1.1 常数据成员
  • 【例 6-12】用带参数的构造函数初始化常数据成员。P179
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
#include <bits\stdc++.h>
using namespace std;

struct Date {
int year, month, day;
};

class Student {
public:
// 用带参数构造函数完成数据成员初始化
Student(int y, int m, int d, int num = 0, char *pname = "no name")
: code(num) {
strcpy_s(name, pname);
name[sizeof(name) - 1] = '\0';
birthday.year = y;
birthday.month = m;
birthday.day = d;
}
// 常成员函数
void PrintStudent() const {
cout << "序号:" << code << "\t姓名:" << name << "\t出生日期:"
<< birthday.year << "-" << birthday.month << "-" << birthday.day
<< endl;
}

private:
const int code; // 常数据成员
char name[20];
Date birthday; // 结构数据成员
};

int main() {
Student stu1(1990, 3, 21, 1001, "陈春");
stu1.PrintStudent();
Student stu2(1985, 10, 1, 1002, "张庆华");
stu2.PrintStudent();
return 0;
}

6.3.2 静态成员

6.3.2.1 静态数据成员
  • 【例 6-14】静态数据成员的声明和初始化。P181
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits\stdc++.h>
using namespace std;

class Counter {
static int num; // 声明私有静态数据成员
public:
void setNum(int i) { num = i; } // 成员函数访问静态数据成员
void showNum() { cout << num << "\t"; } // 成员函数访问静态数据成员
};

int Counter::num = 0; // 定义静态数据成员,置初值0,必须在类外定义

int main() {
Counter a, b;
a.showNum(); // 0
b.showNum(); // 0
a.setNum(10);
a.showNum(); // 10
b.showNum(); // 10
return 0;
}
6.3.2.2 静态成员函数
  • 【例 6-16】某商店经销一种货物。货物购进和卖出时以箱为单位,各箱的重量(质量)不一样,因此,商店需要记录目前库存的总重量。现在编程模拟商店货物购进和卖出的情况。

定义货物类 Goods,数据成员包括一箱货物的重量 weight 和记录货物总重量的静态数据成员 totalWeight。建立一个对象表示购进一箱货物,totalWeight 增加 w;删除一个对象表示卖出一箱货物,totalWeight 减少 w。静态成员函数 TotalWeight 返回当前 totalWeight 的值。为了模拟动态销售情况,Goods 类中包含一个 Goods 类指针 next,用于组成队列,以先进先出方式管理货物。每购进一箱货物,从表尾插入一个结点;每售出一箱货物,从表头删除一个结点。P184

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
66
67
68
69
70
71
#include <bits\stdc++.h>
using namespace std;

class Goods {
public:
Goods(int w) {
weight = w;
totalWeight += w;
}
~Goods() { totalWeight -= weight; }
int Weight() { return weight; }
static int TotalWeight() { // 静态成员函数,返回货物总重量
return totalWeight;
}
Goods *next;

private:
int weight;
static int totalWeight; // 静态数据成员,记录货物总重量
};

int Goods::totalWeight = 0;

void purchase(Goods *&f, Goods *&r, int w) { // 购进货物,从表尾插入结点
Goods *p = new Goods(w);
p->next = NULL;
if (f == NULL) {
f = r = p;
} else {
r->next = p;
r = r->next;
}
}

void sale(Goods *&f, Goods *&r) { // 售出货物,从表头删除结点
if (f == NULL) {
cout << "No any goods!\n";
return;
}
Goods *q = f;
f = f->next;
delete q;
q = NULL;
cout << "sold.\n";
}

int main() {
Goods *front = NULL, *rear = NULL;
int w, choice;
do {
cout << "Please choice:\n";
cout << "Key in 1 is purchase, \nKey in 2 is sale, \nKey in 0 is over.\n";
cin >> choice;
switch (choice) { // 操作选择
case 1: { // 输入,购进一箱货物
cout << "Input weight: "; // 输入一箱货物的重量
cin >> w;
purchase(front, rear, w); // 从表尾插入结点
break;
}
case 2: { // 输入,售出一箱货物
sale(front, rear); // 从表头删除结点
break;
}
case 0: // 输入,结束
break;
}
cout << "Now total weight is: " << Goods::TotalWeight() << endl;
} while (choice);
return 0;
}

6.3.3 友元

6.3.3.2 友元类
  • 【例 6-18】演示友元类。P186
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
#include <bits\stdc++.h>
using namespace std;

class A {
friend class B; // 声明类B是类A的友元
public:
void Display() { cout << x << endl; };

private:
int x;
};

class B {
public:
void Set(int i) { objA.x = i; } // 使用类A对象objA的私有数据成员
void Display() { objA.Display(); } // 调用A类的成员函数

private:
A objA; // 类A对象objA是私有数据成员
};

int main() {
B objB;
objB.Set(100);
objB.Display();
return 0;
}
  • 【例 6-19】修改例 6-18。P186
1
2
3
4
5
6
7
8
9
10
11
12
13
class B {
public:
void Set(A& objA, int i) { objA.x = i; }
void Display(A& objA) { objA.Display(); }
};

int main() {
B objB;
A objA;
objB.Set(objA, 100);
objB.Display(objA);
return 0;
}

6.4 类的包含

  • 【例 6-20】演示友元类。P187
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits\stdc++.h>
using namespace std;

class A {
public:
A(int x) : a(x) {}
int a;
};
class B {
public:
B(int x, int y) : aa(x) { b = y; } // 用参数初始式调用成员类构造函数
// 也可以写成B(int x, int y) : aa(x), b(y) {}
void out() { cout << "aa = " << aa.a << ", b = " << b << endl; }

private:
int b;
A aa; // 类类型成员
};

int main() {
B objB(3, 5);
objB.out(); // aa = 3, b = 5
return 0;
}
  • 【例 6-21】使用 Date 类定义 Student 类。P188
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
#include <bits\stdc++.h>
using namespace std;

class Date {
public:
Date(int y = 2000, int m = 1, int d = 1) {
year = y;
month = m;
day = d;
}
void SetDate(int y, int m, int d) {
year = y;
month = m;
day = d;
}
void PrintDate() const { cout << year << "/" << month << "/" << day << endl; }

private:
int year, month, day;
};

class Student {
public:
// 带参数构造函数完成类成员和自身数据成员的初始化
Student(int y, int m, int d, int num, char *pname = "no name")
: birthday(y, m, d) {
code = num;
strncpy_s(name, pname, sizeof(name));
name[sizeof(name) - 1] = '\0';
}
void SetStudent(int y, int m, int d, int num, char *pname) {
code = num;
strncpy_s(name, pname, sizeof(name));
name[sizeof(name) - 1] = '\0';
cout << name << endl;
}
void PrintStudent() const {
cout << "序号:" << code << "\t姓名:" << name << "\t出生日期:";
birthday.PrintDate(); // 调用类成员的成员函数
}

private:
int code;
char name[20];
Date birthday; // 定义类成员
};

int main() {
Student stu(1985, 10, 1, 1001, "张庆华");
stu.PrintStudent();
return 0;
}

第 7 章 运算符重载

7.1 重载运算符规则

7.1.1 重载运算符的限制

..*::?:sizeof 不能被重载。

7.2 用成员或友元函数重载运算符

7.2.1 用成员函数重载运算符

  • 【例 7-2】建立一个描述三维坐标的类 TriCoor,重载运算符 +++=,实现简单的算术运算。P195
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
#include <bits\stdc++.h>
using namespace std;

class TriCoor {
public:
TriCoor(int mx = 0, int my = 0, int mz = 0) {
x = mx;
y = my;
z = mz;
}
TriCoor operator+(TriCoor t) {
TriCoor temp;
temp.x = x + t.x;
temp.y = y + t.y;
temp.z = z + t.z;
return temp;
}
TriCoor& operator++() {
++x;
++y;
++z;
return *this;
}
TriCoor& operator=(TriCoor t) {
x = t.x;
y = t.y;
z = t.z;
return *this;
}
void show() { cout << x << ", " << y << ", " << z << endl; }
void assign(int mx, int my, int mz) {
x = mx;
y = my;
z = mz;
}

private:
int x, y, z; // 三维坐标值
};

int main() {
TriCoor a(1, 2, 3), b, c;
a.show();
for (int i = 0; i < 5; ++i) {
++b;
}
b.show();
c.assign(3, 3, 3);
c = a + b + c;
c.show();
c = b = a;
c.show();
return 0;
}

7.2.2 用友元函数重载运算符

=()[]-> 不能用友元函数重载。

7.3 几个典型运算符的重载

7.3.1 重载++与–运算符

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 <bits\stdc++.h>
using namespace std;

class Increase {
public:
Increase(int x = 0) { value = x; }
Increase& operator++() { // ++前置
this->value++;
return *this;
}
Increase operator++(int) { // 后置++
// 返回值不用引用,因为不可以返回局部变量
Increase temp = *this;
this->value++;
return temp;
}

int value;
};

int main() {
Increase a;
++a;
cout << a.value << endl;
a++;
cout << a.value << endl;
a.operator++(0); // 等价于a++
cout << a.value << endl;
return 0;
}

7.3.2 重载赋值运算符

  • 【例 7-5】定义 Name 类的重载赋值函数。P201
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
#include <bits\stdc++.h>
using namespace std;

class Name {
public:
Name(char* pN = "\0") {
cout << "Constructing " << pN << endl;
size = strlen(pN);
pName = new char[size + 1];
if (pName != 0) strcpy_s(pName, size + 1, pN);
}
Name(const Name& Obj) { // 定义拷贝构造函数
cout << "Copying " << Obj.pName << " into its own block.\n";
size = Obj.size;
pName = new char[size + 1];
if (pName != 0) strcpy_s(pName, size + 1, Obj.pName);
}
Name& operator=(Name Obj) { // 重载赋值运算符
delete[] pName;
size = Obj.size;
pName = new char[size + 1];
if (pName != 0) strcpy_s(pName, size + 1, Obj.pName);
return *this;
}
~Name() {
cout << "Destructing " << pName << endl;
pName[0] = '\0';
delete[] pName;
pName = NULL;
size = 0;
}

protected:
char* pName;
int size;
};

int main() {
Name Obj1("ZhangSan");
Name Obj2 = Obj1; // 调用拷贝构造函数
Name Obj3("NoName");
Obj3 = Obj2 = Obj1; // 调用重载赋值运算符函数
return 0;
}

7.3.3 重载[]与()运算符 & 7.3.4 重载流插入与留提取运算符

  • 【例 7-7】设计一个功能更强的 Vector 类,其中 重载流插入运算符 << 和流提取运算符 >>,分别用于输出和输入数据;重载 + 运算符用于向量相加,重载 = 运算符用于向量赋值;重载 ==!= 运算符用于判断向量是否相等。P203
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <bits\stdc++.h>
using namespace std;

class Vector {
public:
Vector(int = 1); // 默认长度构造函数
Vector(const int *, int); // 使用数组参数构造函数
Vector(const Vector &); // 拷贝构造函数
~Vector(); // 析构函数
// 重载运算符
int &operator[](int i) const;
int operator()() const;
Vector &operator=(const Vector &);
bool operator==(const Vector &) const;
bool operator!=(const Vector &) const;
friend Vector operator+(const Vector &, const Vector &);
friend ostream &operator<<(ostream &output, const Vector &);
friend istream &operator>>(istream &input, Vector &);

private:
int *v;
int len;
};
// 构造指定长度向量,并初始化数据元素为0
Vector::Vector(int size) {
if (size <= 0 || size > 100) {
cout << "The size of " << size << " is failed!\n";
exit(0);
}
v = new int[size];
for (int i = 0; i < size; ++i) {
v[i] = 0;
}
len = size;
}
// 用整型数组构造向量
Vector::Vector(const int *B, int size) {
if (size <= 0 || size > 100) {
cout << "The size of " << size << " is failed!\n";
exit(0);
}
v = new int[size];
len = size;
for (int i = 0; i < size; ++i) {
v[i] = B[i];
}
}
// 用已有对象复制构造向量
Vector::Vector(const Vector &A) {
len = A();
v = new int[len];
for (int i = 0; i < len; ++i) {
v[i] = A[i];
}
}
// 析构
Vector::~Vector() {
delete[] v;
len = 0;
}
// 返回向量元素
int &Vector::operator[](int i) const {
if (i >= 0 && i < len) return v[i];
cout << "The subscript" << i << " is outside!\n";
exit(0);
}
// 返回向量长度
int Vector::operator()() const { return len; }
// 向量赋值
Vector &Vector::operator=(const Vector &B) {
if (len == B()) {
for (int i = 0; i < len; ++i) {
v[i] = B.v[i];
}
return *this;
} else {
cout << "Operator= failed!\n";
exit(0);
}
}
// 判断两个向量相等
bool Vector::operator==(const Vector &B) const {
if (len == B.len) {
for (int i = 0; i < len; ++i) {
if (v[i] != B.v[i]) return false;
}
} else
return false;
return true;
}
// 判断两个向量不相等
bool Vector::operator!=(const Vector &B) const {
return !(*this == B); // 调用(*this).operator==(B)
}
// 向量相加
Vector operator+(const Vector &A, const Vector &B) {
int size = A();
int *T = new int[size];
if (size == B()) { // 调用B.operator()()返回B.len
for (int i = 0; i < size; ++i) {
T[i] = A.v[i] + B.v[i];
}
return Vector(T, size); // 用数组构造返回对象
} else {
cout << "Operator+ failed!\n";
exit(0);
}
}
// 输出向量
ostream &operator<<(ostream &output, const Vector &A) {
for (int i = 0; i < A.len; ++i) {
output << A.v[i] << " "; // 使用系统版的<<运算符
}
return output;
}
istream &operator>>(istream &input, Vector &A) { // 输入向量
for (int i = 0; i < A(); ++i) {
input >> A.v[i]; // 使用系统版的>>运算符
}
return input;
}

int main() {
int k;
cout << "Input the length of Vector:\n";
cin >> k;
Vector A(k), B(k), C(k); // 构造指定长度向量
cout << "Input the elements of Vector A:\n";
cin >> A; // 调用operator>>(cin, A)
cout << "Input the elements of Vector B:\n";
cin >> B; // 调用operator<<(cin, B)
if (A == B) { // 调用A.operator==(B)
for (int i = 0; i < A(); ++i) {
C[i] = A[i] * 2; // 调用C.operator[](i)和A.operator[](i)
}
} else {
C = A + B; // 调用operator+(A, B)和C.operator=(A + B)
}
// 调用operator<<(cout, A)、operator<<(cout, B)和operator<<(cout, C)
cout << " [ " << A << "]\n+ [ " << B << "]\n= [ " << C << "]\n";
return 0;
}
  • 【例 7-8】设计一个集合类,用无符号整数数组表示集合,重载运算符实现集合的基本运算,以及集合元素的输入、输出。P207
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <bits\stdc++.h>
using namespace std;

class SetType { // 集合类
public:
SetType(unsigned e = 128); // 构造函数
SetType(const SetType &B); // 拷贝构造函数
~SetType(); // 析构函数
SetType operator+=(unsigned x); // 重载+=,把元素x并入集合中
SetType operator=(SetType B); // 重载=,集合变量赋值
SetType operator()(unsigned x = 0); // 重载(),集合置元素x, 默认参数置空
SetType operator+(SetType B); // 重载+,求并集
SetType operator*(SetType B); // 重载*,求交集
SetType operator-(SetType B); // 重载-,求差集
bool operator<=(SetType B); // 重载<=,判包含
bool operator!(); // 重载!,判空集。集合空返回true,否则返回false
friend bool operator<(unsigned x, SetType A); // 重载<,判元素属于集合
friend istream &operator>>(istream &input,
SetType &A); // 重载>>,输入集合元素
friend ostream &operator<<(ostream &output,
SetType &A); // 重载<<,输出集合的全部元素
private:
unsigned *set; // 建立动态数组指针
unsigned n; // 数组长度
unsigned e; // 全集元素个数
};

SetType::SetType(unsigned e) { // 构造函数
n = (e + 31) / 32;
set = new unsigned[n];
for (unsigned i = 0; i < n; ++i) {
set[i] = 0;
}
}
SetType::SetType(const SetType &B) { // 拷贝构造函数
n = B.n;
e = 32 * n;
set = new unsigned[n];
for (unsigned i = 0; i < n; ++i) {
set[i] = B.set[i];
}
}
SetType::~SetType() { // 析构函数
delete[] set;
n = e = 0;
}
SetType SetType::operator+=(unsigned x) { // 重载+=,把元素x并入集合中
unsigned bitMask = 1;
bitMask <<= ((x - 1) % 32);
set[(x - 1) / 32] |= bitMask;
return *this;
}
SetType SetType::operator=(SetType B) { // 重载=,集合变量赋值
for (unsigned i = 0; i < n; ++i) {
set[i] = B.set[i];
}
return *this;
}
SetType SetType::operator()(unsigned x) { // 重载(),集合置元素x,默认参数置空
unsigned bitMask = 1;
for (unsigned i = 0; i < n; ++i) {
set[i] = 0;
}
if (x) {
bitMask <<= ((x - 1) % 32);
set[(x - 1) / 32] |= bitMask;
}
return *this;
}
SetType SetType::operator+(SetType B) { // 重载+,求并集
SetType T(32 * n);
for (unsigned i = 0; i < n; ++i) {
T.set[i] = set[i] | B.set[i];
}
return T;
}
SetType SetType::operator*(SetType B) { // 重载*,求交集
SetType T(32 * n);
for (unsigned i = 0; i < n; ++i) {
T.set[i] = set[i] & B.set[i];
}
return T;
}
SetType SetType::operator-(SetType B) { // 重载-,求差集
SetType T(32 * n);
for (unsigned i = 0; i < n; ++i) {
T.set[i] = set[i] & (~(set[i] & B.set[i]));
}
return T;
}
bool SetType::operator<=(SetType B) { // 重载<=,判包含
for (unsigned i = 0; i < n; ++i) {
if ((set[i] | B.set[i]) != B.set[i]) {
return false;
}
}
return true;
}
bool SetType::operator!() { // 重载!,判空集。集合空返回true,否则返回false
for (unsigned i = 0; i < n; ++i) {
if (set[i]) {
return false;
break;
}
}
return true;
}
bool operator<(unsigned x, SetType A) { // 重载<,判元素属于集合
unsigned bitMask = 1;
bitMask <<= ((x - 1) % 32);
if (A.set[(x - 1) / 32] & bitMask) return true;
return false;
}
istream &operator>>(istream &input, SetType &A) { // 重载>>,输入集合元素
unsigned x;
input >> x;
while (x) {
A += x; // 把元素x并入集合A中
input >> x;
}
return input;
}
ostream &operator<<(ostream &output, SetType &A) {
// 重载<<,输出集合的全部元素
unsigned c, i, bitMask;
if (!A) {
output << "{ }";
return output;
}
output << "{";
for (i = 0; i < A.n; ++i) { // 处理每个数组元素
bitMask = 1; // 掩码
for (c = 1; c <= 32; ++c) { // 按位处理
if (A.set[i] & bitMask) {
output << i * 32 + c << ", ";
}
bitMask <<= 1;
}
}
output << "\b\b} "; // 擦除最后一个元素之后的逗号
return output;
}

int main() {
SetType setA, setB, setC;
unsigned x;
cout << "Input the elements of setA, 1-128, until input 0:\n";
cin >> setA; // 输入setA的元素
cout << "Input the elements of setB, 1-128, until input 0:\n";
cin >> setB; // 输入setB的元素
cout << "setA = " << setA << endl; // 输出setA的元素
cout << "setB = " << setB << endl; // 输出setB的元素
cout << "Input x: ";
cin >> x;
setA += x; // 把元素x并入setA中
cout << "Put " << x << " in setA = " << setA << endl;
setC = setA + setB; // 求并集
cout << "setC = setA + setB = " << setC << endl;
setC = setA * setB; // 求交集
cout << "setC = setA * setB = " << setC << endl;
setC = setA - setB; // 求差集
cout << "setC = setA - setB = " << setC << endl;
if (setA <= setB) { // 判断setA是否包含于setB
cout << "setA <= setB\n";
} else {
cout << "not setA <= setB\n";
}
cout << "Input x: ";
cin >> x;
if (x < setA) { // 判断元素x是否属于setA
cout << x << " in " << setA << endl;
} else {
cout << x << " not in " << setA << endl;
}
setC = setA + setB + setC; // 多个集合变量运算
cout << "setC = setA + setB + setC = " << setC << endl;
setC(); // 置setC为空集
cout << "setC = " << setC << endl;
return 0;
}

7.4 类类型转换

7.4.2 使用类型转换函数

  • 【例 7-9】简单串类与字符串之间的类型转换。P213
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits\stdc++.h>
using namespace std;

class String {
char* data;
int size;

public:
String(char* s) {
size = strlen(s);
data = new char(size + 1);
strcpy_s(data, size + 1, s);
}
operator char*() const { return data; } // 类型转换函数
};

int main() {
String sObj = "hello";
char* sVar = sObj; // 把String型对象赋给字符串变量,进行了类型转换
cout << sVar << endl;
return 0;
}
  • 【例 7-10】实现简单有理数计算。

有理数类 Rational 定义了两个数据成员:分子 Numerator 和分母 Denominator。有三个构造函数可以用不同形式的初值构造对象:构造一个初值为 0 的有理数,用分子、分母构造有理数,以及用实数构造有理数。带参数构造函数同时具备把基本数据类型参数转换成 Rational 类型的功能。一个类型转换的成员函数可以把 Rational 类对象转换为 double 型数据。还有两个友元函数:重载运算符 + 用于两个有理数的求和,重载运算符 << 用于输出有理数。gcd 函数求最大公约数,用于对有理数进行约分。P213

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
66
67
68
#include <bits\stdc++.h>
using namespace std;

class Rational {
public:
Rational() { Numerator = Denominator = 0; } // 构造等于0的对象
Rational(int n, int d = 1) { // 用分子、分母构造对象
if (d == 1) { // 分母等于1
Numerator = n; // 分子
Denominator = d; // 分母
} else { // 分母不等于1的有理数
int g = gcd(n, d); // 求分子、分母的最大公约数
Numerator = n / g; // 约分
Denominator = d / g;
}
}
Rational(double x) { // 用实数构造对象
int a = int(x * 1e5); // 分子
int b = int(1e5); // 分母
int g = gcd(a, b); // 求分子、分母的最大公约数
Numerator = a / g; // 约分
Denominator = b / g;
}
operator double() { // 类型转换函数,把Rational类型转换成double型
return double(Numerator) / double(Denominator);
}
friend Rational operator+(const Rational &, const Rational &); // 重载+
friend ostream &operator<<(ostream &, const Rational &); // 重载<<
private:
int Numerator, Denominator;
};

int gcd(int a, int b) { // 求最大公约数
if (b == 0) {
return a;
} else {
return gcd(b, a % b);
}
}

Rational operator+(const Rational &r1, const Rational &r2) { // 重载运算符+
int n = r1.Numerator * r2.Denominator + r1.Denominator * r2.Numerator;
int d = r1.Denominator * r2.Denominator;
return Rational(n, d);
}
ostream &operator<<(ostream &output, const Rational &x) { // 重载运算符<<
output << x.Numerator;
if (x.Denominator != 1) {
output << "/" << x.Denominator;
}
return output;
}

int main() {
Rational a(2, 4);
Rational b = 0.3;
Rational c = a + b; // 调用友元重载运算符+和默认重载运算符=
// 调用类型转换operator double函数,以实数形式显示0.5 + 0.3 = 0.8
cout << double(a) << " + " << double(b) << " = " << double(c) << endl;
// 调用重载operator<<函数,以分数形式显示1/2 + 3/10 = 4/5
cout << a << " + " << b << " = " << c << endl;
double x = b; // 用operator double函数对b进行类型转换
c = x + 1 + 0.6; // 用Rational(double)对表达式进行类型转换
cout << x << " + 1 + 0.6 = " << double(c) << endl; // 0.3 + 1 + 0.6 = 1.9
cout << Rational(x) << " + " << Rational(1) << " + " << Rational(0.6) << " = "
<< c << endl; // 3/10 + 1 + 3/5 = 19/10
return 0;
}

第 8 章 继承

8.4 继承的应用实例

  • 【例 8-9】考察点、圆与圆柱体的层次结构。首先定义点类 Point,然后从 Point 类派生圆类 Circle,最后从 Circle 类派生圆柱体类 Cylinder。P230
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <bits\stdc++.h>
using namespace std;

class Point {
friend ostream &operator<<(ostream &, const Point &);

public:
Point(int a = 0, int b = 0) {
setPoint(a, b); // 带默认参数的构造函数,调用成员函数对x, y进行初始化
}
void setPoint(int a, int b) { // 对点坐标数据赋值
x = a;
y = b;
}
int getX() const { return x; }
int getY() const { return y; }

protected:
int x, y; // Point类的数据成员
};
// 重载<<,输出对象数据
ostream &operator<<(ostream &output, const Point &p) {
output << "[" << p.x << ", " << p.y << "]";
return output;
}

class Circle : public Point {
friend ostream &operator<<(ostream &, const Circle &); // 友元函数
public:
Circle(double r = 0.0, int a = 0, int b = 0) : Point(a, b) { // 构造函数
setRadius(r);
}
void setRadius(double r) { radius = (r >= 0 ? r : 0); } // 置半径值
double getRadius() const { return radius; } // 返回半径
double area() const { return 3.14159 * radius * radius; } // 返回面积
protected:
double radius; // 数据成员,半径
};
// 输出圆心坐标和半径
ostream &operator<<(ostream &output, const Circle &c) {
output << "Center = [" << c.x << ", " << c.y
<< "]; Radius = " << setiosflags(ios::fixed | ios::showpoint)
<< setprecision(2) << c.radius;
return output;
}

class Cylinder : public Circle {
friend ostream &operator<<(ostream &, const Cylinder &); // 友元函数
public:
Cylinder(double h = 0.0, double r = 0.0, int x = 0, int y = 0)
: Circle(r, x, y) { // 构造函数
setHeight(h);
}
void setHeight(double h) { height = (h >= 0 ? h : 0); } // 置高度值
double getHeight() const { return height; } // 返回高度
double area() const { // 返回表面积
return 2 * Circle::area() + 2 * 3.14159 * radius * height;
}
double volume() const { return Circle::area() * height; } // 返回体积
protected:
double height; // 数据成员,高度
};
// 输出数据成员圆心坐标、半径和高度
ostream &operator<<(ostream &output, const Cylinder &cy) {
output << "Center = [" << cy.x << ", " << cy.y
<< "]; Radius = " << setiosflags(ios::fixed | ios::showpoint)
<< setprecision(2) << cy.radius << "; Height = " << cy.height << endl;
return output;
}

int main() {
Point p(72, 115); // 定义点对象并初始化
cout << "The initial location of p is " << p << ".\n";
p.setPoint(10, 10); // 置点的新值
cout << "\nThe new location of p is " << p << ".\n"; // 输出数据
Circle c(2.5, 37, 43); // 定义圆对象并初始化
cout << "\nThe initial location and radius of c are\n"
<< c << "\nArea = " << c.area() << endl;
c.setRadius(4.25); // 置圆的新值
c.setPoint(2, 2); // 输出圆心坐标和圆面积
cout << "\nThe new location and radius of c are\n"
<< c << "\nArea = " << c.area() << endl;
Cylinder cyl(5.7, 2.5, 12, 23); // 定义圆柱体对象并初始化
// 输出圆柱体各数据和表面积,体积
cout << "\nThe initial location, radius and height of cyl are\n"
<< cyl << "Area = " << cyl.area() << "\nVolume = " << cyl.volume()
<< endl;
cyl.setHeight(10); // 置圆柱体的新值
cyl.setRadius(4.25);
cyl.setPoint(2, 2);
cout << "\nThe new location, radius and height of cyl are\n"
<< cyl << "Area = " << cyl.area() << "\nVolume = " << cyl.volume()
<< endl;
return 0;
}

第 9 章 虚函数与多态性

9.2 类指针的关系

9.2.1 用基类指针引用派生类对象

  • 【例 9-1】使用基类指针引用派生类对象。P246
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 <bits\stdc++.h>
using namespace std;

class AClass {
char name[20];

public:
void putName(char* s) { strcpy_s(name, s); }
void showName() const { cout << name << endl; }
};

class BClass : public AClass {
char phoneNum[20];

public:
void putPhone(char* num) { strcpy_s(phoneNum, num); }
void showPhone() const { cout << phoneNum << endl; }
};

int main() {
AClass AObj;
BClass BObj;
AClass* AP = &AObj;
AP->putName("Wang Xiao Hua");
AP->showName();
AP = &BObj; // 基类指针指向派生类对象
AP->putName("Chen ming"); // 调用基类成员函数
AP->showName();
BObj.putPhone("5555_12345678"); // 调用派生类成员函数
((BClass*)AP)->showPhone(); // 对基类指针进行强制类型转换
return 0;
}

9.2.2 用派生类指针引用基类对象

  • 【例 9-2】日期时间程序。由日期类 Date 派生日期时间类 DateTime。P247
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
#include <bits\stdc++.h>
using namespace std;

class Date {
public:
Date(int y, int m, int d) { SetDate(y, m, d); }
void SetDate(int y, int m, int d) {
year = y;
month = m;
day = d;
}
void Print() const { cout << year << "/" << month << "/" << day << " "; }

protected:
int year, month, day;
};

class DateTime : public Date {
public:
DateTime(int y, int m, int d, int h, int mi, int s) : Date(y, m, d) {
SetTime(h, mi, s);
}
void SetTime(int h, int mi, int s) {
hour = h;
minute = mi;
second = s;
}
void Print() const { cout << hour << ":" << minute << ":" << second << endl; }

private:
int hour, minute, second;
};

int main() {
DateTime dt(2003, 1, 1, 12, 30, 0);
DateTime *pdt = &dt;
((Date)dt).Print(); // 对象类型转换,调用基类成员函数
dt.Print();
((Date *)pdt)->Print(); // 对象指针类型转换,调用基类成员函数
pdt->Print();
return 0;
}
  • 【例 9-3】重写例 9-2 程序,在派生类的 DateTime::Print 中调用基类同名成员函数 Date::Print。P248
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
class DateTime : public Date {
public:
DateTime(int y, int m, int d, int h, int mi, int s) : Date(y, m, d) {
SetTime(h, mi, s);
}
void SetTime(int h, int mi, int s) {
hour = h;
minute = mi;
second = s;
}
void Print() const {
((Date *)this)->Print(); // 对this指针作类型转换
//((Date)(*this)).Print(); // 对this对象进行类型转换
// Date::Print(); // 显式地指示执行基类版本的成员函数
// 三行都是等价的
cout << hour << ":" << minute << ":" << second << endl;
}

private:
int hour, minute, second;
};

int main() {
DateTime dt(2003, 1, 1, 12, 30, 0);
dt.Print();
return 0;
}

9.5 虚函数与多态性的应用

9.5.1 一个实例 & 9.5.2 异质链表

  • 【例 9-12】本例程利用虚函数和多态性计算雇员工资。P259

  • 【例 9-13】用指针数组构造异质链表。说明了长度为 6 的 Employee* 类型数组。首先,用数组中的每个元素创建不同派生类的动态对象。然后,循环语句用遍历数组的方式,调用不同版本的 print 函数输出各对象的数据。earnings 函数也可以用同样的方式调用。P264

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <bits\stdc++.h>
using namespace std;

class Employee {
public:
Employee(const long k, const char* str) {
number = k;
strcpy_s(name, 20, str); // 字符串赋值
}
virtual ~Employee() { name[0] = '\0'; } // 虚析构函数
const char* getName() const { return name; }
const long getNumber() const { return number; }
virtual double earnings() const = 0; // 纯虚函数,协算月薪
virtual void print() const { // 虚函数,输出编号、姓名
cout << number << setw(20) << name;
}
Employee* next; // 增加一个指针成员

protected:
long number;
char name[20];
};

class Manager : public Employee {
public:
Manager(const long, const char*, double = 0.0);
~Manager() {}
void setMonthlySalary(double); // 置月薪
virtual double earnings() const; // 计算管理人员的月薪
virtual void print() const; // 输出管理人员的信息

private:
double monthlySalary; // 月薪
};
Manager::Manager(const long k, const char* str, double sal) : Employee(k, str) {
setMonthlySalary(sal);
}
void Manager::setMonthlySalary(double sal) {
monthlySalary = sal > 0 ? sal : 0;
}
double Manager::earnings() const { return monthlySalary; }
void Manager::print() const {
Employee::print(); // 调用基类版本的函数,输出编号和姓名
cout << setw(16) << "Manager\n"; // 输出管理人员的月薪
cout << "\tearned $" << monthlySalary << endl;
}

class HourlyWorker : public Employee {
public:
HourlyWorker(const long, const char*, double = 0.0, int = 0);
~HourlyWorker() {}
void setWage(double); // 置时薪
void setHours(int); // 置工时
virtual double earnings() const; // 计算计时工人的月薪
virtual void print() const; // 输出计时工人的月薪

private:
double wage;
double hours;
};
HourlyWorker::HourlyWorker(const long k, const char* str, double w, int h)
: Employee(k, str) {
setWage(w);
setHours(h);
}
void HourlyWorker::setWage(double w) { // 置时薪,判断时薪的合法性
wage = w > 0 ? w : 0;
}
void HourlyWorker::setHours(int h) { // 置工时,判断工时的合法性
hours = h >= 0 && h <= 16 * 31 ? h : 0;
}
double HourlyWorker::earnings() const { // 计算基本工资
if (hours <= 8 * 22)
return wage * hours;
else
// 计算基本工资和加班工资
return wage * (8 * 22) + (hours - 8 * 22) * wage * 1.5;
}
void HourlyWorker::print() const {
Employee::print(); // 输出编号、姓名
cout << setw(16) << "Hours Worker\n"; // 输出计时工人的月薪
cout << "\twagePerHour " << wage << " Hours " << hours;
cout << " earned $" << earnings() << endl;
}

class PieceWorker : public Employee {
public:
PieceWorker(const long, const char*, double = 0.0, int = 0);
~PieceWorker() {}
void setWage(double); // 置件薪
void setQuantity(int); // 置工件数
virtual double earnings() const;
virtual void print() const;

private:
double wagePerPiece; // 件薪
int quantity; // 工件数
};
PieceWorker::PieceWorker(const long k, const char* str, double w, int q)
: Employee(k, str) {
setWage(w);
setQuantity(q);
}
void PieceWorker::setWage(double w) { wagePerPiece = w > 0 ? w : 0; } // 置件薪
void PieceWorker::setQuantity(int q) { // 置月完成的工件数
quantity = q > 0 ? q : 0;
}
double PieceWorker::earnings() const { // 计算月薪
return quantity * wagePerPiece;
}
void PieceWorker::print() const { // 输出计件工人的信息
Employee::print();
cout << setw(16) << "Piece Worker\n";
cout << "\twagePerPiece " << wagePerPiece << " quantity " << quantity;
cout << " earned $" << earnings() << endl;
}

int main() {
Employee* employ[6];
employ[0] = new Manager(10135, "Cheng ShaoHua", 1200);
employ[1] = new Manager(10201, "Yan HaiFeng", 5300);
employ[2] = new HourlyWorker(30712, "Zhao XiaoMing", 5, 8 * 20);
employ[3] = new HourlyWorker(30649, "Gao DongSheng", 4.5, 10 * 30);
employ[4] = new PieceWorker(20382, "Xiu LiWei", 0.5, 2850);
employ[5] = new PieceWorker(20496, "Huang DongLin", 0.75, 1850);
cout << setiosflags(ios::fixed | ios::showpoint) << setprecision(2);
for (int i = 0; i < 5; ++i) {
employ[i]->print();
}
for (int i = 0; i < 5; ++i) {
cout << employ[i]->getName() << " " << employ[i]->earnings() << endl;
}
return 0;
}
  • 【例 9-14】动态异质链表。P265
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
void AddFront(Employee*& h, Employee*& t) {  // 在表头中插入结点
t->next = h;
h = t;
}

int main() {
Employee *ernpHead = NULL, *ptr;
ptr = new Manager(10135, "Cheng ShaoHua", 1200); // 建立第一个结点
AddFront(ernpHead, ptr); // 插入表头
ptr = new HourlyWorker(30712, "Zhao XiaoMing", 5, 8 * 20); // 建立第二个结点
AddFront(ernpHead, ptr); // 插入表头
ptr = new PieceWorker(20382, "Xiu LiWei", 0.5, 2850); // 建立第三个结点
AddFront(ernpHead, ptr); // 插入表头
ptr = ernpHead;
while (ptr) { // 遍历链表
ptr->print();
ptr = ptr->next;
}
ptr = ernpHead;
while (ptr) {
cout << ptr->getName() << " " << ptr->earnings() << endl;
ptr = ptr->next;
}
return 0;
}

第 10 章 模板

10.3 类模板

10.3.1 类模板与模板类

  • 【例 10-4】一个数组类模板。P272
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
#include <bits\stdc++.h>
using namespace std;

template <typename T> // 定义类模板
class Array {
public:
Array(int s);
virtual ~Array() { delete[] element; }
virtual const T& Entry(int index) const { return element[index]; }
virtual void Enter(int index, const T& value) { element[index] = value; }

int size;

private:
T* element; // 数据成员是T类型指针
};

template <typename T>
Array<T>::Array(int s) { // 成员函数是函数模板
if (s > 1) {
size = s;
} else {
size = 1;
}
element = new T[size];
}

int main() {
Array<int> intAry(5); // 用int进行实例化,建立模板类对象
for (int i = 0; i < 5; ++i) {
intAry.Enter(i, i);
}
cout << "Integer Array:\n";
for (int i = 0; i < 5; ++i) {
cout << intAry.Entry(i) << "\t";
}
cout << endl;
Array<double> douAry(5); // 用double进行实例化,建立模板类对象
for (int i = 0; i < 5; ++i) {
douAry.Enter(i, (i + 1) * 0.35);
}
cout << "Double Array:\n";
for (int i = 0; i < 5; ++i) {
cout << douAry.Entry(i) << "\t";
}
cout << endl;
return 0;
}

10.3.3 在类层次中的类模板

  • 【例 10-6】从类模板 Array<T> 派生一个可以自定义下标范围,并可以对数组元素下标进行合法性检查的安全数组类模板 BoundArray<T>。P275
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
template <typename T>
class BoundArray : public Array<T> {
// 定义类模板BoundArray<T>,继承类模板Array<T>
public:
BoundArray(int low = 0, int height = 1); // 定义数组的下界和上界
virtual const T& Entry(int index) const;
virtual void Enter(int index, const T& value);

private:
int min;
};

template <typename T>
BoundArray<T>::BoundArray(int low, int height) : Array<T>(height - low + 1) {
if (height - low < 0) { // 界限检查
cout << "Beyond the bounds of Array.\n";
exit(1);
}
min = low;
}

template <typename T>
const T& BoundArray<T>::Entry(int index) const {
if (index < min || index > min + Array<T>::size - 1) { // 下标合法性检查
cout << "Beyond the bounds of index.\n";
exit(1);
}
return Array<T>::Entry(index - min); // 调用基类版本成员函数
}

template <typename T>
void BoundArray<T>::Enter(int index, const T& value) {
if (index < min || index > min + Array<T>::size - 1) { // 下标合法性检查
cout << "Beyond the bounds of index.\n";
exit(1);
}
Array<T>::Enter(index - min, value); // 调用基类版本成员函数
}

int main() {
int low = 1, height = 10;
BoundArray<int> b(low, height);
for (int i = low; i <= height; ++i) {
b.Enter(i, i * 2);
}
cout << "BoundArray:\n";
for (int i = low; i <= height; ++i) {
cout << "b[" << i << "] = " << b.Entry(i) << "\t";
if (i % 5 == 0) {
cout << endl;
}
}
return 0;
}
  • 【例 10-7】从类模板派生模板类。P276
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 <bits\stdc++.h>
using namespace std;

template <typename T> // 定义类模板
class A {
public:
A(T x) { t = x; }
void out() { cout << t << endl; }

protected:
T t;
};

class B : public A<int> { // 实例化基类的类属参数,派生模板类
public:
B(int a, double x) : A<int>(a) { y = x; }
void out() {
A<int>::out();
cout << y << endl;
}

protected:
double y;
};

int main() {
A<int> a(123); // 定义基类对象
a.out();
B b(789, 5.16); // 定义派生类对象
b.out();
return 0;
}

10.3.4 类模板与友元

10.3.4.2 模板类的友元类
  • 【例 10-8】为复数类模板定义重载运算符的友元函数。P278
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
#include <bits\stdc++.h>
using namespace std;

template <typename T>
class Complex {
public:
Complex(T r = 0, T i = 0) {
real = r;
image = i;
}
T real, image;

private:
template <typename T2>
friend Complex<T> operator+(const Complex<T> c1, const Complex<T> c2);
template <typename T2>
friend Complex<T> operator-(const Complex<T> &c1, const Complex<T> &c2);
template <typename T2>
friend Complex<T> operator-(const Complex<T> &c);
template <typename T2>
friend ostream &operator<<(ostream &output, const Complex<T> &c);
};

template <typename T>
Complex<T> operator+(const Complex<T> c1, const Complex<T> c2) {
T r = c1.real + c2.real;
T i = c1.image + c2.image;
return Complex<T>(r, i);
}

template <typename T>
Complex<T> operator-(const Complex<T> &c1, const Complex<T> &c2) {
T r = c1.real - c2.real;
T i = c1.image - c2.image;
return Complex<T>(r, i);
}

template <typename T>
Complex<T> operator-(const Complex<T> &c) {
return Complex<T>(-c.real, -c.image);
}

template <typename T>
ostream &operator<<(ostream &output, const Complex<T> &c) {
output << "(" << c.real << ", " << c.image << ")";
return output;
}

int main() {
Complex<double> c1(2.5, 3.7), c2(4.2, 6.5);
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
cout << "c1 + c2 = " << c1 + c2 << endl;
cout << "c1 - c2 = " << c1 - c2 << endl;
cout << "-c1 = " << -c1 << endl;
return 0;
}

C++ 程序设计基础
https://blog.gtbcamp.cn/article/cpp-fundamentals/
作者
Great Thunder Brother
发布于
2022年9月11日
更新于
2025年8月14日
许可协议