面向对象程序设计
2025-04-06 01:01:39 # code

由于c++理论课一直没有去上,实验课一开始又投机取巧没有按题目要求写,导致现在实验课上得有点头痛……决定开一篇把之前的实验重新规范写一下,放一些模板上来,上到哪写到哪吧。


指针

找数组最大值

code

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 <iostream>
using namespace std;
// 调用指针
int FindArrayMax(int a[], int n)
{
int max = *a;
for (int i = 0; i < n; i++)
{
if (*(a + i) > max)
{
max = *(a + i);
}
}
return max;
}
int main()
{
const int MAXN = 10;
int i, n;
int a[MAXN];
cin >> n;
for (i = 0; i < n; i++)
{
cin >> a[i];
}
cout << FindArrayMax(a, n) << endl;
return 0;
}

基础指针调用。

动态矩阵

题目描述

未知一个整数矩阵的大小,在程序运行时才会输入矩阵的行数m和列数n

要求使用指针,结合new方法,动态创建一个二维数组,并求出该矩阵的最小值和最大值,可以使用数组下标法。

不能先创建一个超大矩阵,然后只使用矩阵的一部分空间来进行数据访问、

创建的矩阵大小必须和输入的行数m和列数n一样

code

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 <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
for (int u = 1; u <= t; u++)
{
int m, n;
cin >> m >> n;
int maxx;
int minn;
// 建立动态二维数组
int **k = new int *[m];
for (int i = 0; i < m; i++)
{
k[i] = new int[n];
for (int j = 0; j < n; j++)
{
cin >> k[i][j];
// 初始化最大最小值
if (i == 0 && j == 0)
{
maxx = k[i][j];
minn = k[i][j];
}
// 更新最大最小值
else
{
maxx = max(maxx, k[i][j]);
minn = min(minn, k[i][j]);
}
}
}
cout << minn << " " << maxx << "\n";
// 释放数组空间
for (int i = 0; i < m; i++)
{
delete[] k[i];
}
delete[] k;
}
return 0;
}

主要是熟悉如何建立动态数组和释放空间。

密钥加密法

题目描述

有一种方式是使用密钥进行加密的方法,就是对明文的每个字符使用密钥上对应的密码进行加密,最终得到密文

例如明文是abcde,密钥是234,那么加密方法就是a对应密钥的2,也就是a偏移2位转化为c;明文b对应密钥的3,就是b偏移3位转化为e,同理c偏移4位转化为g。这时候密钥已经使用完,那么又重头开始使用。因此明文的d对应密钥的2,转化为f,明文的e对应密钥的3转化为h。所以明文abcde,密钥234,经过加密后得到密文是cegfh

如果字母偏移的位数超过26个字母范围,则循环偏移,例如字母z偏移2位,就是转化为b,同理字母x偏移5位就是转化为c

要求:使用三个指针p、q、s分别指向明文、密钥和密文,然后使用指针p和q来访问每个位置的字符,进行加密得到密文存储在指针s指向的位置。

除了变量定义和输入数据,其他过程都不能使用数组下标法,必须使用三个指针来访问明文、密钥和密文。

提示:当指针q已经移动到密钥的末尾,但明文仍然没有结束,那么q就跳回密钥头

code

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
#include <cstring>
#include <iostream>
using namespace std;
void solve(char &p, char &q, char &s)
{
// 用一个int存储密文值,防止字符越界
int add = p + q;
if (p >= 'a' && p <= 'z')
{
while (add > 'z')
{
add -= 26;
}
}
else
{
while (add > 'Z')
{
add -= 26;
}
}
s = add;
}
int main()
{
int t;
cin >> t;
for (int u = 0; u < t; u++)
{
char s[22];
char c[22];
cin >> s >> c;
int k = 0;
// 密钥预处理
for (int i = 0; i < strlen(c); i++)
{
c[i] -= '0';
c[i] %= 26;
}
for (int i = 0; i < strlen(s); i++)
{
char ans;
solve(*(s + i), *(c + k), ans);
cout << ans;
k++;
// 跳回密钥头
if (k == strlen(c))
{
k = 0;
}
}
cout << "\n";
}
return 0;
}

其实就是比较繁琐的一道题……没什么难度,注意’z‘的ASCII值为122,但ASCII值只到127,直接加可能会超出这个范围导致变为负数,循环条件不成立导致输出有问题,我一开始就是踩到这个坑de了很久bug……


引用与结构

谁是老二

题目描述

定义一个结构体,包含年月日,表示一个学生的出生日期。然后在一群学生的出生日期中找出谁的出生日期排行第二

要求:出生日期的存储必须使用结构体,不能使用其他类型的数据结构。

要求程序全过程对出生日期的输入、访问、输出都必须使用结构。

code

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
#include <algorithm>
#include <iostream>
using namespace std;
struct bir
{
int y, m, d;
// 运算符重载
bool operator<(const bir &that) const
{
if (y != that.y)
{
return y < that.y;
}
else
{
if (m != that.m)
{
return m < that.m;
}
else
{
return d < that.d;
}
}
}
};
int main()
{
int n;
cin >> n;
bir a[n + 1];
for (int i = 0; i < n; i++)
{
cin >> a[i].y >> a[i].m >> a[i].d;
}
sort(a, a + n);
cout << a[1].y << "-" << a[1].m << "-" << a[1].d;
return 0;
}

留个结构体运算符重载模板。

抄袭查找

题目描述

已知一群学生的考试试卷,要求对试卷内容进行对比,查找是否有抄袭。

每张试卷包含:学号(整数类型)、题目1答案(字符串类型)、题目2答案(字符串类型)、题目3答案(字符串类型)

要求:使用结构体来存储试卷的信息。定义一个函数,返回值为一个整数,参数是两个结构体指针,函数操作是比较两张试卷的每道题目的答案,如果相同题号的答案相似度超过90%,那么就认为有抄袭,函数返回抄袭题号,否则返回0。相似度是指在同一题目中,两个答案的逐个位置上的字符两两比较,相同的数量大于等于任一个答案的长度的90%,就认为抄袭。

code

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
#include <iostream>
using namespace std;
struct aa
{
string s;
string ans[3];
};
int solve(aa &a, aa &b)
{
int p = 0;
for (int i = 0; i < 3; i++)
{
// 记得是取较小答案长度
int len = min(a.ans[i].length(), b.ans[i].length());
int cnt = 0;
for (int j = 0; j < len; j++)
{
if (a.ans[i][j] == b.ans[i][j])
{
cnt++;
}
}
// 懒得用double,字符串长度不超过100,这样写精度已经够了
if ((cnt * 10) / len >= 9)
{
p = i + 1;
break;
}
}
// 返回抄袭题号,未抄袭返回0
return p;
}
int main()
{
int n;
cin >> n;
aa a[n + 1];
for (int i = 0; i < n; i++)
{
cin >> a[i].s;
for (int j = 0; j < 3; j++)
{
cin >> a[i].ans[j];
}
}
// 两两检查
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
int p = solve(*(a + i), *(a + j));
if (p != 0)
{
cout << a[i].s << " " << a[j].s << " " << p << "\n";
}
}
}
return 0;
}

又是一道烦烦的繁琐题…练一下吧。抄什么袭!


类与对象

身体评估

题目描述

评估成年人身体健康有多个指标,包括BMI、体脂率等

设计一个身体健康,包含私有成员:姓名、身高(米)、体重(公斤),腰围(厘米),实现两个公有方法如下:

BMI方法,返回BMI数值(整数,四舍五入到个位),计算公式BMI= 体重 / 身高的平方

体脂率方法,返回体脂率数值(浮点数),计算过程如下:

1)参数a=腰围(cm)×0.74

2)参数b=体重(kg)×0.082+34.89

3)体脂肪重量(kg)=a-b

4)体脂率 = 体脂肪重量÷体重

其它方法根据需要自行定义

code

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
#include <iomanip>
#include <iostream>
using namespace std;
class kk
{
private:
string s;
double h, w, y;

public:
// set函数给类内参数赋值
void set(string ss, double hh, double ww, double yy);
// get函数访问类内参数
string gets();
// 计算各种值……
int bmi();
double aa();
double bb();
double dd();
};
void kk::set(string ss, double hh, double ww, double yy)
{
s = ss;
h = hh;
w = ww;
y = yy;
}
string kk::gets()
{
return s;
}
int kk::bmi()
{
// 这里bmi需要四舍五入,不可以直接用int
double bmii = w / (h * h);
int bmi = bmii / 1;
if (bmii - bmi >= 0.5)
{
bmi++;
}
return bmi;
}
double kk::aa()
{
return 0.74 * y;
}
double kk::bb()
{
return w * 0.082 + 34.89;
}
double kk::dd()
{
return (aa() - bb()) / w;
}
int main()
{
int t;
cin >> t;
for (int u = 0; u < t; u++)
{
string s;
double h, w, y;
cin >> s >> h >> w >> y;
kk a;
a.set(s, h, w, y);
cout << a.gets() << "的BMI指数为" << a.bmi() << "--体脂率为" << fixed << setprecision(2) << a.dd() << "\n";
}
return 0;
}

掌握class内如何构造函数,如何给类内参数赋值,如何用get访问类内参数。其他操作和结构体类似。


构造与析构

分数类

题目描述

完成下列分数类的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
class CFraction
{
private:
int fz, fm;
public:
CFraction(int fz_val, int fm_val);
CFraction add(const CFraction &r);
CFraction sub(const CFraction &r);
CFraction mul(const CFraction &r);
CFraction div(const CFraction &r);
int getGCD(); // 求对象的分子和分母的最大公约数
void print();
};

求两数a、b的最大公约数可采用辗转相除法,又称欧几里得算法,其步骤为:

  1. 交换a, b使a > b;
  2. 用a除b得到余数r,若r=0,则b为最大公约数,退出.
  3. 若r不为0,则用b代替a, r代替b,此时a,b都比上一次的小,问题规模缩小了;
  4. 继续第2步。

注意:如果分母是1的话,也按“分子/1”的方式输出。

code

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
#include <iostream>
using namespace std;
class cf
{
private:
int m, s;

public:
// 构造
cf(int ss, int mm);
cf add(const cf &that);
cf sub(const cf &that);
cf mul(const cf &that);
cf div(const cf &that);
int gcd();
void print();
};
int cf::gcd()
{
int a = s;
int b = m;
while (b != 0)
{
int t = a % b;
a = b;
b = t;
}
return a;
}
cf::cf(int ss, int mm)
{
s = ss;
m = mm;
}
cf cf::add(const cf &that)
{
int ss = m * that.s + that.m * s;
int mm = m * that.m;
return cf(ss, mm);
}
cf cf::sub(const cf &that)
{
int ss = s * that.m - that.s * m;
int mm = m * that.m;
return cf(ss, mm);
}
cf cf::mul(const cf &that)
{
int ss = s * that.s;
int mm = m * that.m;
return cf(ss, mm);
}
cf cf::div(const cf &that)
{
int ss = s * that.m;
int mm = m * that.s;
return cf(ss, mm);
}
void cf::print()
{
// 输出前处理
int g = gcd();
s /= g;
m /= g;
if (m < 0)
{
m *= -1;
s *= -1;
}
cout << s << "/" << m << "\n";
}
int main()
{
int t;
cin >> t;
for (int i = 0; i < t; i++)
{
int s1, s2, m1, m2;
scanf("%d/%d", &s1, &m1);
scanf("%d/%d", &s2, &m2);
cf f1(s1, m1), f2(s2, m2);
cf add = f1.add(f2);
add.print();
cf sub = f1.sub(f2);
sub.print();
cf mul = f1.mul(f2);
mul.print();
cf div = f1.div(f2);
div.print();
cout << "\n";
}
return 0;
}

构造构造构造……意会一下吧。

Point_Array

题目描述

上面是我们曾经练习过的一个习题,请在原来代码的基础上作以下修改:

  1. 增加自写的析构函数;
  2. 将getDisTo方法的参数修改为getDisTo(const Point &p);
  3. 根据输出的内容修改相应的构造函数。

然后在主函数中根据用户输入的数目建立Point数组,求出数组内距离最大的两个点之间的距离值。

code

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
#include <cmath>
#include <iomanip>
#include <iostream>
using namespace std;
class point
{
private:
double x, y;

public:
~point();
point();
point(double xx, double yy);
double getx();
double gety();
void set(double xx, double yy);
void setx(double xx);
void sety(double yy);
double dis(const point &p);
};
point::~point()
{
cout << "Distructor.\n";
}
point::point()
{
x = 0;
y = 0;
cout << "Constructor.\n";
}
point::point(double xx, double yy)
{
set(xx, yy);
}
void point::set(double xx, double yy)
{
setx(xx);
sety(yy);
}
void point::setx(double xx)
{
x = xx;
}
void point::sety(double yy)
{
y = yy;
}
double point::dis(const point &p)
{
double dx = x - p.x;
double dy = y - p.y;
return sqrt(dx * dx + dy * dy);
}
double point::getx()
{
return x;
}
double point::gety()
{
return y;
}
int main()
{
int t;
cin >> t;
for (int u = 0; u < t; u++)
{
int n;
cin >> n;
point *p = new point[n];
for (int i = 0; i < n; i++)
{
double x, y;
cin >> x >> y;
p[i].set(x, y);
}
double maxx = 0;
int a = 0;
int b = 0;
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
double dd = p[i].dis(p[j]);
if (dd > maxx)
{
maxx = dd;
a = i;
b = j;
}
}
}
cout << "The longest distance is " << fixed << setprecision(2) << maxx << ",between p[" << a << "] and p[" << b << "].\n";
// 析构
delete[] p;
}
return 0;
}

就多了个析构函数的步骤。

Stack

题目描述

上面是栈类的定义,是一种具有先进后出特点的线性表,请根据注释,完成类中所有方法的实现,并在主函数中测试之。

堆栈类的说明如下:

  1. 堆栈的数据实际上是保存在数组a中,而a开始是一个指针,在初始化时,根据实际需求将a动态创建为数组,数组长度根据构造函数的参数决定。
  2. size实际上就是数组的长度,当使用无参构造则size为10,当使用有参构造则size为s、
  3. top表示数组下标,也表示数组中下一个存放数据的空白位置。
  4. push操作表示堆栈的数组存放一个数据,例如一开始数组为空,则top为0,当有数据要入栈时,把数据存放在a[top]的位置,然后top加1指向下一个空白位置、数据进栈只能从栈顶进。
  5. pop操作表示一个数据要出栈,数据出栈只能从栈顶出,先把top减1指向栈顶数据,然后把数据返回。
  6. 判断堆栈空的条件是top是否等于0,判断堆栈满的条件是top是否等于size

code

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
#include <iostream>
using namespace std;
class cstack
{
public:
cstack();
cstack(int s);
int get(int index);
void push(int n);
int emptyy();
int full();
int pop();
~cstack();

private:
int *a;
int size;
int top;
};
cstack::cstack()
{
a = nullptr;
size = 0;
top = 0;
cout << "Constructor.\n";
}
cstack::cstack(int s)
{
a = new int[s];
size = s;
top = 0;
cout << "Constructor.\n";
}
int cstack::get(int index)
{
return *(a + index);
}
void cstack::push(int n)
{
*(a + top) = n;
top++;
}
int cstack::emptyy()
{
if (top == 0)
{
return 1;
}
return 0;
}
int cstack::full()
{
if (top == size)
{
return 1;
}
return 0;
}
int cstack::pop()
{
top--;
return *(a + top);
}
cstack::~cstack()
{
// 记得释放数组
delete[] a;
cout << "Destructor.\n";
}
int main()
{
int t;
cin >> t;
for (int u = 0; u < t; u++)
{
int n;
cin >> n;
cstack *s = new cstack(n);
while (!s->full())
{
int x;
cin >> x;
s->push(x);
}
while (!s->emptyy())
{
// 不能输出行末空格……
if (!s->full())
{
cout << " ";
}
cout << s->pop();
}
cout << "\n";
delete s;
}
return 0;
}

原理差不多,练练手吧。好想用stack……

Prev
2025-04-06 01:01:39 # code
Next