slacr_

Just to record my life and thoughts.
笔记/编程/杂乱/极简

[C++Lab]

Apr 16, 2023C_C++4116 words in 27 min

C++实验课

lab2_3

题目

补充实验:成绩表里找同学
【问题描述】
编写一个程序,读入 N 个同学的姓名和语数外三门功课的单科成绩,对其按照一定的排序规则排序形成一张成绩表(先按总分从高到低排序,总分相同则按语文成绩由高到低排序,仍然相同则按数学成绩由高到低排序,若总分和单科成绩均相同则最后按姓名的字典序排序)。最后给定一个数字 K(K<=N) ,输出在这张排好序的成绩表中位置在第 K 位的同学的姓名和总分。
字典序举例说明:
abc > abd
a > aa
要求:用C++对象数组表示学生数据。采用多文件结构,类的声明放在头文件如student.h,类的实现放在源文件如student.cpp。
【输入形式】
第一行输入一个正整数 N(1<=N<=1024),代表接下来将录入 N 个学生的成绩。
接下来 N 行录入 N 条学生的成绩记录,每条记录依次为姓名,语文成绩,数学成绩,英语成绩,这四个字段以空格分隔。姓名为字符串(仅包含小写字母,不含空格,长度<=19),学生之间不会重名。三科成绩均为整数(0~100)。
最后输入一个正整数 K(1<=K<=N),代表需要在成绩表中从前往后找到的位置。
【输出形式】
在成绩表中从前往后处在第 K 位的同学的姓名和总分,中间以空格分隔。
【样例输入1】
2
tom 90 91 92
lucy 91 90 92
1
【样例输出1】
lucy 273
【样例输入2】
4
lily 90 100 88
jack 87 79 95
hanz 90 89 71
david 90 89 71
4
【样例输出2】
hanz 250

解答

lab2_3_Stu.h C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Student {
public:
string name;
int yu;
int shu;
int wai;
int sum;
Student() {}
Student(string name, int yu, int shu, int wai) :name(name), yu(yu), shu(shu), wai(wai) {}
void setInfo();
};
lab2_3_Stu.cpp C++
1
2
3
4
5
6
7
8
#include "lab2_3_Stu.h"
void Student::setInfo() {
cin >> this->name;
cin >> this->yu;
cin >> this->shu;
cin >> this->wai;
sum = this->yu + this->wai + this->shu;
}
lab2_3.cpp C++
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
#include <iostream>
#include <string>
using namespace std;
#include <vector>
#include <algorithm>
#include "lab2_3_Stu.h"
class Mypred {
public:
bool operator()(Student s1, Student s2) {
if (s1.sum == s2.sum) {
return s1.name < s2.name;
}
else
{
return s1.sum > s2.sum;
}
}
};
class ScoreSheet {
public:
int numOfStu;
vector<Student> vstus;
void initSheet() {
cin >> numOfStu;
vstus.resize(numOfStu);
for (int i = 0; i < numOfStu; i++)
{
Student stu;
stu.setInfo();
vstus[i] = stu;
}
sort(vstus.begin(), vstus.end(), Mypred());
}
void queeryCode() {
int code;
cin >> code;
cout << vstus[code-1].name << " " << vstus[code-1].sum << endl;
}
};
int main() {
ScoreSheet ss;
ss.initSheet();
ss.queeryCode();
return 0;
}

分文件, 我这里只分了一个
分文件方法:
.h 中放声明, .cpp中放实现, 主函数中引用 .h
或者.hpp中放声明和实现, 主函数中引用 .hpp
这题很典型, vector容器储存, sort算法排序传个谓词进去, 不同排序的方式由谓词实现.

lab3_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
编写一个学生和教师数据输入和显示程序。其中,学生数据有编号、姓名、班级和成绩,教师数据有编号、姓名、职称和部门。
要求:
(1)将编号、姓名输入和显示设计成一个类person;
(2)设计类person的派生类:学生类student和教师类teacher;
(3)主函数中分别定义一个学生对象和教师对象进行测试。
【输入形式】
程序参考的输入(输入数据前面为提示文字):
Input id:1
Input name:zhangsan
Input class:1
Input score:90
Input id:2
Input name:lisi
Input title:teacher
Input department:computer
【输出形式】
程序参考的输出:
Student's info:
Id:1
Name:zhangsan
Class:1
Score:90
Teacher's info:
Id:2
Name:lisi
Title:teacher
Department:computer

解答

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  <iostream>
#include <string>
using namespace std;
class person
{
public:
person()
{
cout << "Input id:";
cin >> id;
cout << "Input name:";
cin >> name;
}
void display()
{
cout << "Id:" << id << endl;
cout << "Name:" << name << endl;
}
private:
string id;
string name;
};
// 填充部分
class student : public person{
public:
student() {
cout << "Input class:";
cin >> classNum;
cout << "Input score:";
cin >> score;
}
void display() {
cout << "Student's info:" << endl;
person::display();
cout << "Class:" << classNum << endl;
cout << "Score:" << score << endl;
}
private:
string classNum;
int score;
};
class teacher : public person {
public:
teacher() {
cout << "Input title:";
cin >> title;
cout << "Input department:";
cin >> department;
}
void display() {
cout << "Teacher's info:" << endl;
person::display();
cout << "Title:" << title << endl;
cout << "Department:" << department << endl;
}
private:
string title;
string department;
};
//
int main()
{
student stu;
teacher tea;
stu.display();
tea.display();
return 0;
}

一开始想了半天调用父类的private成员要用友元, 又不能更改题目, 后来才发现不是给了公共display接口么. 写派生类多关注公共接口, 私有的不需要管.

lab3_2

题目

补充:字符串统计排序,继承系统string类
【问题描述】
编写一个程序,接收用户输入的一个字符串(可以包含空格),统计其中所有出现过的所有字符,并按照频率高低的顺序排列输出。频率相同的字符按输入顺序输出。
【输入形式】
用户在第一行输入一个字符串,以回车结束输入。
【输出形式】
程序统计字符串中出现的所有字符,然后按照字符出现频率大小排序输出,频率相同时,按输入顺序输出。输出形式规定为每行输出4个字符数据,输出格式为:字符-出现次数。每个字符-出现次数输出中间用一个空格分隔,每行末尾没有空格。程序输出结尾有一个回车。
【样例输入】
The job requires an agile mind.
【样例输出】
#-5 e-4 i-3 r-2
a-2 n-2 T-1 h-1
j-1 o-1 b-1 q-1
u-1 s-1 g-1 l-1
m-1 d-1 .-1 #表示空格(在程序请输出空格,而不是字符"#",这里只是表示而已。)
【样例说明】
用户首先输入字符串The job requires an agile mind. 程序统计完毕之后按照每行4个统计结果输出,字符串中有5个空格,所以输出为#-5,#表示空格。字符’b’和’T’出现次数同为1,因为输入时’b’先于’T’输入,所以输出时也先打印’b’的统计信息。

解答

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
#include  <iostream>
#include <string>
using namespace std;
class MyChar
{
public:
MyChar() :num(0) {}
char ch; //字符
int num; //出现次数
};
class MyString : public string
{
public:
void input() {
getline(cin, *this); //输入一行,支持空格
sum = 0;
}
void putchar(char ch); //放入字符
void compute();
void output();
private:
MyChar chars[300]; //字符计数
int sum; //已有总数
};
void MyString::putchar(char ch)
{
// 存在有这个字符的话num++
for (int i = 0; i < sum; i++)
{
if (chars[i].ch == ch)
{
chars[i].num++;
return;
}
}
//没有这个字符的话初始化这个字符
chars[sum].ch = ch;
chars[sum].num++;
sum++;
}
// 填空一
void MyString::compute() {
//分析字符出现频次并排序
//遍历输入的字符串
for (int i = 0; i < (*this).size(); i++) {
putchar((*this)[i]);
}
//按照字符个数排序chars[], 冒泡排序, 如果两个字符数量相同, 冒泡排序是安全的, 不会改变位置.
for (int i = 0; i < sum - 1; i++) {
for (int j = 0; j < sum - i - 1; j++) {
if (chars[j].num < chars[j + 1].num) {
//交换
MyChar temp = chars[j];
chars[j] = chars[j + 1];
chars[j + 1] = temp;
}
}
}
}
//
// 填空二
void MyString::output()
{
//输出分析结果
for (int i = 1; i <= sum; i++)
{
cout << chars[i - 1].ch << "-" << chars[i - 1].num << " ";
if (i % 4 == 0) cout << endl;
}
}
//使得程序正确执行
int main()
{
MyString s;
s.input();
s.compute();
s.output();
return 0;
}

要看懂每一个函数是做什么, getline(cin, *this);, 用到了多态, *this是string的派生类, 父类指针指向子类对象.
chars[]中的是引用对象, 交换位置的时候交换引用就行了, 而不是值交换.

lab5

两道分析题

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
#include   <iostream>
#include <complex>
using namespace std;
class Base
{
public:
Base() { cout << "Base-ctor" << endl; }
~Base() { cout << "Base-dtor" << endl; }
virtual void f(int) { cout << "Base::f(int)" << endl; }
virtual void f(double) { cout << "Base::f(double)" << endl; }
virtual void g(int i = 10) { cout << "Base::g()" << i << endl; }
};
class Derived : public Base
{
public:
Derived() { cout << "Derived-ctor" << endl; }
~Derived() { cout << "Derived-dtor" << endl; }
void f(complex<double>) {
cout << "Derived::f(complex)" << endl;
}
void g(int i = 20) {
cout << "Derived::g()" << i << endl;
}
};
int main()
{
cout << sizeof(Base) << endl;
cout << sizeof(Derived) << endl;
Base b;
Derived d;
Base* pb = new Derived;
b.f(1.0);
d.f(1.0);
pb->f(1.0);
b.g();
d.g();
pb->g();
delete pb;
return 0;
}


/*
分析:

8 // vfptr 64位 占 8 字节, 32位占 4 字节
8 // 同上
Base-ctor // 基类构造
Base-ctor // 派生类先构造基类再构造派生类
Derived-ctor
Base-ctor // new 创建派生类再堆区, 同上先构造基类再构造派生类
Derived-ctor
Base::f(double) // 本类函数调用
Derived::f(complex) // 本类函数调用
Base::f(double) // pb是基类指针指向派生类对象,调用f函数,先再Derived类中的Base中匹配发现无, 再利用继承的vfptr向vftable中寻找发现匹配基类中的函数, 调用, 基类f函数没有重写. 虚表中还是基类函数的地址
Base::g()10 // 本类函数调用
Derived::g()20 // 本类函数调用
Derived::g()10 // pb依旧先在Derived中的Base下寻找g发现无, 利用继承的vfptr向vftable中寻找, 因为Derived重写了Base的g, 继承时基类虚表的g函数地址被改变, 从而调用派生类的g
Base-dtor // delete释放, pb调用派生类下基类析构
Derived-dtor // 编译器自动调用清理内存, 按主函数调用栈中本地变量的创建顺序相反, 析构d, 先析构派生类再析构基类
Base-dtor
Base-dtor //析构b
*/
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
#include   <iostream>
using namespace std;
class Base
{
public:
Base() :data(count)
{
cout << "Base-ctor" << endl;
++count;
}
~Base()
{
cout << "Base-dtor" << endl;
--count;
}
static int count;
int data;
};
int Base::count;
class Derived : public Base
{
public:
Derived() :data(count), data1(data)
{
cout << "Derived-ctor" << endl;
++count;
}
~Derived()
{
cout << "Derived-dtor" << endl;
--count;
}
static int count;
int data1;
int data;
};
int Derived::count = 10;
int main()
{
cout << sizeof(Base) << endl;
cout << sizeof(Derived) << endl;
Base* pb = new Derived[3];
cout << pb[2].data << endl;
cout << ((static_cast<Derived*>(pb)) + 2)->data1 << endl;
delete[] pb;
cout << Base::count << endl;
cout << Derived::count << endl;
return 0;
}


/*
4 // static 静态变量, 在编译时确定内存, 不包括在类中, Base中含有一个int, 分配的大小为 4 字节,
12 // Base中的data, 和Derived中的data, data1, 三个int , 12字节
Base-ctor // new 堆区初始化Derived实例, 三次, 先构造基类在构造自身
Derived-ctor
Base-ctor
Derived-ctor
Base-ctor
Derived-ctor
10 // pb基类指针指向派生类对象, 基类在派生类的内存分布在最前, pb访问到的是派生类的成员, 并且有偏移量限制, 不能访问派生类的内存.
-842150451 // static_cast将pb转换为Derived*, 这是可以的, 或者用更安全的dynamic_cast, 只要改变偏移量, 不过基类要改成多态类型的抽象类,
// 这题有点问题, 搞不懂为什么要用data初始化data1, data而又没有初始化值, 所以是不确定的内存值, 初始化了data1就是正常值
Base-dtor // 调用pb指向的Derived中Base下的析构三次
Base-dtor //
Base-dtor
0 // 静态变量默认初始化0
13 // Derived 构造三次 13, 没有调用Derived析构.内存泄漏, 建议把基类析构声明成虚函数就可以动态绑定按顺序析构派生类.

*/

lab6

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
#include  <iostream>
#include <cmath>
using namespace std;
class CFraction
{
private:
int nume; // 分子
int deno; // 分母
public:
CFraction(int nu = 0, int de = 1) :nume(nu), deno(de) {}
void simplify();

//输入输出的重载
friend istream &operator>>(istream &in, CFraction &x);
friend ostream &operator<<(ostream &out, CFraction x);

CFraction operator+(const CFraction &c2){
int ldeno = deno * c2.deno;
int lnume = nume * c2.deno + c2.nume * deno;
CFraction temp(lnume, ldeno);
temp.simplify();
return temp;

} //两个分数相加,结果要化简
CFraction operator-(const CFraction &c2){
int ldeno = deno * c2.deno;
int lnume = nume * c2.deno - c2.nume * deno;
CFraction temp(lnume, ldeno);

temp.simplify();
return temp;
} //两个分数相减,结果要化简
CFraction operator*(const CFraction &c2){
int ldeno = deno * c2.deno;
int lnume = nume * c2.nume;
CFraction temp(lnume, ldeno);

temp.simplify();
return temp;
} //两个分数相乘,结果要化简
CFraction operator/(const CFraction &c2){
CFraction rotate(c2.deno, c2.nume);
return *this*rotate;

} //两个分数相除,结果要化简

};

istream& operator>>(istream &in, CFraction &x){
char temp;
in >> x.nume >> temp >> x.deno;
return in;
}

ostream& operator<<(ostream &out, CFraction x){
if (x.deno == 1){ out << x.nume << endl; }
else out << (x.nume < 0 ? showpos : noshowpos) << x.nume << "/" << noshowpos << x.deno;
return out;
}

inline int gcd(int a, int b) {
a = abs(a);
b = abs(b);
int r;
while (b > 0) {
r = a % b;
a = b;
b = r;
}
return a;
}

void CFraction::simplify() {
int common = gcd(deno, nume);
nume /= common;
deno /= common;
}

//CFraction operator+(const CFraction &c1, const CFraction &c2){
// int deno = c1.deno * c2.deno;
// int nume = c1.nume * c2.deno + c2.nume * c1.deno;
// CFraction temp(nume, deno);
// temp.simplify();
// return temp;
//
//} //两个分数相加,结果要化简
//
//CFraction operator-(const CFraction &c1, const CFraction &c2){
// int deno = c1.deno * c2.deno;
// int nume = c1.nume * c2.deno - c2.nume * c1.deno;
// CFraction temp(nume, deno);
//
// temp.simplify();
// return temp;
//
//} //两个分数相减,结果要化简
//CFraction operator*(const CFraction &c1, const CFraction &c2){
// int deno = c1.deno * c2.deno;
// int nume = c1.nume * c2.nume;
// CFraction temp(nume, deno);
//
// temp.simplify();
// return temp;
//} //两个分数相乘,结果要化简
//CFraction operator/(const CFraction &c1, const CFraction &c2){
// CFraction rotate(c2.deno, c2.nume);
// return c1*rotate;
//
//} //两个分数相除,结果要化简

int main()
{
CFraction x, y, s;
cout << "Input x: ";
cin >> x;
cout << "Input y: ";
cin >> y;
s = x + y;
cout << "x+y=" << s << endl;
s = x - y;
cout << "x-y=" << s << endl;
s = x*y;
cout << "x*y=" << s << endl;
s = x / y;
cout << "x/y=" << s << endl;
system("pause");
return 0;
}
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
#include<iostream>
#include<string>
#include<string.h>
using namespace std;
#include <vector>


class Matrix {
public:
vector<int> v;
int i, j;
// 构造函数
Matrix(int i, int j) :i(i), j(j) {
v.resize(i * j);
}
// 赋值运算
Matrix& operator=(const Matrix& m) {
copy(m.v.begin(), m.v.end(), v.begin());
j = m.j;
i = m.i;
return *this;
}
// 重载 <<
friend ostream& operator<<(ostream& out, Matrix& m) {
for (int p = 0; p < m.i; p++) {
for (int q = 0; q < m.j; q++) {
out << m.v[p * m.j + q] << " ";
}
out << endl;
}
return out;
}
// 重载 >>
friend istream& operator>> (istream& in, Matrix& m) {
for (int i = 0; i < m.v.size(); i++) cin >> m.v[i];
return in;
}
// 重载 +=
void operator+=(const Matrix& m) {
if (i != m.i || j != m.j) cout << "ERROR!" << endl;
else {
for (int i = 0; i < m.v.size(); i++) v[i] += m.v[i];
cout << *this << endl;
}
}
// 重载 *=
void operator*=(const Matrix& m) {
if (j != m.i) cout << "ERROR!" << endl;
else {
Matrix res(i, m.j);
// 矩阵乘, 比如 ij 矩阵 乘 jk 矩阵得到 ik 矩阵, 求ik矩阵只需遍历i行k列, 然后遍历j次求和得到 m[i][k]
for (int p = 0; p < res.i; p++) {
for (int q = 0; q <res.j; q++) {
for (int r = 0; r < j; r++) {
res.v[p * res.j + q] = res.v[p * res.j + q] + v[p * j + r] * m.v[r * m.j + q];
}
}
}
copy(res.v.begin(), res.v.end(), v.begin());
cout << *this << endl;
}
}
// 重载()
int operator()(int a, int b) {
return v[a * j + b];
}
};

int main() {
int row1, col1, row2, col2;
cin >> row1 >> col1;
Matrix m1(row1, col1);
cin >> m1;

cin >> row2 >> col2;
Matrix m2(row2, col2);
cin >> m2;

cout << m1(row1 / 2, col1 / 2) << endl;
m1 *= m2;
m1 += m2;
m1 = m2;
cout << m1 << endl;
return 0;
}
  • Author:

    slacr_

  • Copyright:

  • Published:

    April 16, 2023

  • Updated:

    April 16, 2023

Buy me a cup of coffee ☕.

1000000