面向对象程序设计第三次上机作业Debug心路历程
前言
由于西安疫情不可抗力因素,期中考试被迫推迟
世界第一拉扯.icu没有拉扯!新校长万岁!!!
原本定于期中考试之后再写的OOP上机作业被挪到了这个周(请叫我时间管理大师)
然后就有了,在写屎山代码中的一些奇奇怪怪的bug
新手上路,如有错误欢迎指出
1.Container类
题目描述:
定义抽象基类 Container, 至少含有2个纯虚函数:getVolume, getArea,分别实现计算Container体积,计算面积的功能。由它派生出3个派生类:Sphere(球体),Cylinder(圆柱体)、Cube(正方体),派生类需要实现基类中的纯虚函数,实现动态多态。
要求:
1.绘制与本例相关的UML图
2.每个类的头文件和成员方法实现文件分开
3.写一个名为main.cpp的文件,在其中验证动态多态的效果;
4.利用基于vector容器(自行查阅资料,了解vector容器的用法),存储与子类对象相关的数据,通过对容器内元素进行遍历,调用getVolume, getArea,体现动态多态
迅速标记关键词:抽象基类、纯虚函数、动态多态
那就先写个Container的基类
//container.h
class Container {
public:
virtual double getVolume()=0;
virtual double getArea()=0;
virtual void display()=0;
};
看起来并没有什么毛病(实际有一个非常致命的问题,但在目前还不影响)
这里运用了虚函数,老师上课讲过(照着PPT抄过来就是了)
接着实现成员方法
//container.cpp
#include"container.h"
#include<iostream>
using namespace std;
double Container::getArea(){
return 0;
}
double Container::getVolume(){
return 0;
}
void Container::display(){
cout << "Container volume: " << getVolume() << endl;
cout << "Container area: " << getArea()<<endl;
}
自我感觉良好,接着往下写
//sphere.h
#include"container.h"
class Sphere :public Container {
private:
double radius;
public:
Sphere(double r) {
radius = r;
}
double getVolume();
double getArea();
void display();
};
是的,依旧是看起来没什么问题,但是同样的致命错误,目前不影响
//sphere.cpp
#include"sphere.h"
#include<iostream>
using namespace std;
double Sphere::getVolume(){
return 4.0 / 3 * 3.14 * radius * radius * radius;
}
double Sphere::getArea(){
return 4 * 3.14 * radius * radius;
}
void Sphere::display() {
cout << "Sphere volume: " << getVolume() << endl;
cout << "Sphere area: " << getArea() << endl;
}
我这个人有个毛病,不喜欢全写完代码再找bug,于是我在写完Sphere类之后就建了main.cpp来测试我的基类和派生类Sphere有无问题
//main.cpp
#include"container.h"
#include"sphere.h"
#include<iostream>
using namespace std;
void calculator(Container* s) {
s->display();
}
int main() {
Container* s1 = new Sphere(4);
calculator(s1);
delete s1;
return 0;
}
之前说了那个致命问题在目前是不影响的, 所以显然,在测试的时候也没有问题,正常输出了Sphere的体积Volume和面积Area
没毛病,很简单,喜大普奔,转战第二个派生类Cylinder
//cylinder.h
#include"container.h"
class Cylinder :public Container {
private:
double radius,height;
public:
Cylinder(double r, double h) {
radius = r;
height = h;
}
double getVolume();
double getArea();
void display();
};
//cylinder.cpp
#include"cylinder.h"
#include<iostream>
using namespace std;
double Cylinder::getVolume(){
return 3.14 * radius * radius * height;
}
double Cylinder::getArea(){
return 3.14 * radius * radius * 2 + 3.14 * 2 * radius * height;
}
void Cylinder::display(){
cout << "Cylinder volume: " << getVolume() << endl;
cout << "Cylinder area: " << getArea() << endl;
}
放到main.cpp中测试
//main.cpp
#include"container.h"
#include"sphere.h"
#include"cylinder.h"
#include<iostream>
using namespace std;
void calculator(Container* s) {
s->display();
}
int main() {
Container* s1 = new Sphere(4);
Container* s2 = new Cylinder(4,5);
calculator(s1);
calculator(s2);
delete s1;
delete s2;
return 0;
}
好的,之前说过的问题在此时暴露了
根本问题是什么,是代码为C2011的类型重定义
确实最后在main.cpp中引用头文件的时候Container类的定义被重复引用了两次
此时我就想着那把cylinder.h引用container.h改成引用sphere.h应该就能解决这个问题,然后再main.cpp中只引用cylinder.h
后来转念一想,当我写cube的时候,那我是不是应该引用cylinder.h,然后再main.cpp中引用cube.h,天哪,这也太麻烦了,一定是哪里出了问题
#pragma once 好熟悉的东西
哦是我每次新建.h文件式都要删掉的那一行
删掉!!!我为什么要把他删掉!!!
于是在每一个.h文件中重新写上
#pargma once
喜大普奔2.0
趁热打铁完成cube类
//cube.h
#pragma once
#include"container.h"
class Cube :public Container {
private:
double length;
public:
Cube(double l) {
length = l;
}
double getVolume();
double getArea();
void display();
};
//cube.cpp
#include"cube.h"
#include<iostream>
using namespace std;
double Cube::getVolume() {
return length * length * length;
}
double Cube::getArea() {
return 6 * length * length;
}
void Cube::display() {
cout << "Cube volume: " << getVolume() << endl;
cout << "Cube area: " << getArea() << endl;
}
最后附上完整的main.cpp、运行结果以及UML图
//main.cpp
#include"container.h"
#include"sphere.h"
#include"cylinder.h"
#include"cube.h"
#include<iostream>
using namespace std;
void calculator(Container* s) {
s->display();
}
int main() {
Container* s1 = new Sphere(4);
Container* s2 = new Cylinder(4,5);
Container* s3 = new Cube(2.5);
calculator(s1);
calculator(s2);
calculator(s3);
delete s1;
delete s2;
delete s3;
return 0;
}
小结
本题完成的尚可,完成了要求1、2、3,唯一不足的是没有使用vector容器,不过在下面的题中运用到了,满分10分的话给自己打个8分吧!
2.Matrix类
接下来转战本次上次作业中最折磨本人的一道题
首先在这里感谢zeroy同学为我答疑解惑
题目描述:
定义 矩阵类Matrix,包含整数型私有成员变量 row 和 col,分别代表矩阵的行数和列数。矩阵元素为float类型,且存储于通过new 操作符获取的 一维数组,定义该类的 构造函数、拷贝构造函数、析构函数,重载 赋值运算符=、加法运算符+和乘法运算符* ,使之实现矩阵加法和乘法运算;重载<<和>>运算符,使之能用于该矩阵的输入和输出。
矩阵类Matrix需要实现一个静态成员函数randomPopulate() , 实现一个矩阵元素随机填充方法,便于测试(如此无需手动输入元素)
迅速标记关键词:new 操作符、运算符重载
后来又同学在群里提问了关于静态成员函数的问题,于是题目变成了这样
定义矩阵类 Matrix,包含整数型私有成员变量 row 和 col,分别代表矩阵的行数和列数。矩阵元素为float类型,且存储于通过new 操作符获取的一维数组,定义该类的构造函数、拷贝构造函数、析构函数,重载赋值运算符=、加法运算符+和乘法运算符*,使之实现矩阵加法和乘法运算;重载<<和>>运算符,使之能用于该矩阵的输入和输出。
矩阵类Matrix需要实现一个静态成员函数randomPopulate(), 实现一个矩阵元素随机填充方法,便于测试(如此无需手动输入元素) 原题目描述中要求:“矩阵类Matrix需要实现一个静态成员函数randomPopulate()”,现订正为:矩阵类Matrix需要实现一个成员函数randomPopulate() 之所以要求定义这么一个函数,是为了方便对Matrix类进行测试。如果有同学觉得有其他更好更方便的测试手段,例如:手动输入、读取文件等,也是可以的。只要有助于测试Matrix类能正确工作即可。
首先先定义Matrix类
class Matrix {
public:
int row, col;
float* arr;
Matrix() {
row = 0;
col = 0;
arr = new float[row * col];
}
Matrix(int r, int c) {
row = r;
col = c;
arr = new float[row * col];
}
Matrix(const Matrix& A) {
row = A.row;
col = A.col;
arr = new float[row * col];
for (int i = 0; i < row * col; i++) {
arr[i] = A.arr[i];
}
}
void operator =(const Matrix& A) {
row = A.row;
col = A.col;
arr = new float[row * col];
for (int i = 0; i < row * col; i++) {
arr[i] = A.arr[i];
}
}
~Matrix() {
delete[]arr;//delete申请的堆资源
}
friend Matrix operator+(const Matrix& A, const Matrix& B);
friend Matrix operator*(const Matrix& A, const Matrix& B);
friend istream& operator>>(istream& in, Matrix& A);
friend ostream& operator<<(ostream& out, Matrix& A);
};
然后是重载运算符的操作
Matrix operator+(const Matrix& A, const Matrix& B) {
Matrix C;
for (int i = 0; i < A.row * A.col; i++) {
C.arr[i] = A.arr[i] + B.arr[i];
}
return C;
}
Matrix operator*(const Matrix& A, const Matrix& B) {
Matrix C;
float result = 0;
for (int i = 0; i < A.row; i++) {
for (int j = 0; j <B.col; j++) {
for (int k = 0; k < A.col; k++) {
result += A.arr[i * A.col + k] * B.arr[k * B.col + j];
}
C.arr[i * C.col + j] = result;
result = 0;
}
}
return C;
}
istream& operator>>(istream& in, Matrix& A) {
in >> A.row >> A.col;
for (int i = 0; i < A.row; i++) {
for (int j = 0; j < A.col; j++) {
in >> A.arr[i * A.col + j];
}
}
return in;
}
ostream& operator<<(ostream& out, Matrix& A) {
out << "row: "<<A.row << " col: " << A.col << endl;
for (int i = 0; i < A.row; i++) {
for (int j = 0; j < A.col; j++) {
out << A.arr[i * A.col + j] << " ";
}
out << endl;
}
return out;
}
最后是main函数
int main() {
Matrix A, B, C, D, E;
cin >> A;
cin >> B;
cin >> C;
D = A + B;
cout << D << endl;
E = A * C;
cout << E << endl;
A.~Matrix();
B.~Matrix();
C.~Matrix();
D.~Matrix();
E.~Matrix();
return 0;
}
当然,出问题了
咨询了zeroy同学,得到的结果是
好有道理!
但是
好人一生平安
改了
但没完全改
我裂开了!!!
VScode 是好的,VS上就不行
所以,其实一切问题出在了我对指针运用的不熟练上,最原始的问题是拷贝构造函数没有分配内存。
经历了一天又一个上午的折磨,这个代码终于能正常跑了
附上能正常运行的代码以及与运行结果
//main.cpp
#include<iostream>
using namespace std;
class Matrix {
public:
int row, col;
float* arr;
Matrix() {
row = 0;
col = 0;
arr = new float[row * col];
}
Matrix(int r, int c) {
row = r;
col = c;
arr = new float[row * col];
}
Matrix(const Matrix& A) {
row = A.row;
col = A.col;
arr = new float[row * col];
for (int i = 0; i < row * col; i++) {
arr[i] = A.arr[i];
}
}
void operator =(const Matrix& A) {
row = A.row;
col = A.col;
arr = new float[row * col];
for (int i = 0; i < row * col; i++) {
arr[i] = A.arr[i];
}
}
~Matrix() {
delete[]arr;//delete申请的堆资源
}
friend Matrix operator+(const Matrix& A, const Matrix& B);
friend Matrix operator*(const Matrix& A, const Matrix& B);
friend istream& operator>>(istream& in, Matrix& A);
friend ostream& operator<<(ostream& out, Matrix& A);
};
Matrix operator+(const Matrix& A, const Matrix& B) {
Matrix C = Matrix(A.row, A.col);
for (int i = 0; i < A.row * A.col; i++) {
C.arr[i] = A.arr[i] + B.arr[i];
}
return C;
}
Matrix operator*(const Matrix& A, const Matrix& B) {
Matrix C = Matrix(A.row, B.col);
float result = 0;
for (int i = 0; i < A.row; i++) {
for (int j = 0; j < B.col; j++) {
for (int k = 0; k < A.col; k++) {
result += A.arr[i * A.col + k] * B.arr[k * B.col + j];
}
C.arr[i * C.col + j] = result;
result = 0;
}
}
return C;
}
istream& operator>>(istream& in, Matrix& A) {
in >> A.row >> A.col;
A = Matrix(A.row, A.col);
for (int i = 0; i < A.row; i++) {
for (int j = 0; j < A.col; j++) {
in >> A.arr[i * A.col + j];
}
}
return in;
}
ostream& operator<<(ostream& out, Matrix& A) {
out << "row: " << A.row << " col: " << A.col << endl;
for (int i = 0; i < A.row; i++) {
for (int j = 0; j < A.col; j++) {
out << A.arr[i * A.col + j] << " ";
}
out << endl;
}
return out;
}
int main() {
Matrix A, B, C, D, E;
cin >> A;
cin >> B;
cin >> C;
D = A + B;
cout << D << endl;
E = A * C;
cout << E << endl;
return 0;
}
小结
其实写这个题的时候还出了很多问题,比如我的重载运算符,在编译器里总是有语法问题,然后关掉vs再打开又可以了,就很头疼,但最后在同学的帮助下还是完成啦!!!
满分10分给自己打6.5分,缺点是还是用的手动输入,没有写一个函数来完成。
3.Student 类
这个题其实完成的还是比较顺利的,基本没有出大问题
题目描述:
创建一个存有学生信息的文本型文件 student_info.txt,包含至少 10 名学生信息,该文件
格式如下:
姓名 性别 学号 成绩
Alice F 西电格式学号 90
Bob M 西电格式学号 87
…
定义类 Student,声明非静态私有成员变量:姓名、性别、学号、成绩,声明静态成员变
量:学生总人数和总成绩、定义静态成员函数get_average,支持计算所有学生的平均成绩。
该类需重载<<操作符,支持对 Student 类对象的格式化输出(例如:每个属性域宽 15, 各属性均采用左对齐等)。Student 类的其他成员变量、成员函数可按需求自行设计添加。
编写一个普通函数 student_info_parser(string file_name, vector<Student> &students),读取student_info.txt,填充存储学生类对象的容器 students
创建主函数文件 main.cpp,测试上述要求功能的正确性。
这个题,跟上学期大一的时候那个程序设计基础的有个题型很像,只不过之前那个是用C语言写的文件读写,这个使用C++写,并且有类这个东西
(考察的几乎是一样的)
//student.h
#pragma once
#ifndef _STUDENT_H
#define _STUDENT_H
#include<iostream>
#include<fstream>
#include<string>
#include<cstdlib>
#include<vector>
#include<iomanip>
using namespace std;
class Student {
private:
string name;
char sex;
string number;
double score;
static int totalAmount;//静态成员变量:总人数
static double totalScore;//静态成员变量:总成绩
public:
Student();
Student(string n, char s, int num, double sco);
static double get_average();//静态成员函数:计算所有学生的平均成绩
friend void student_info_parser(string file_name, vector<Student>& students);
friend ostream& operator<<(ostream& os, const Student& stu);
};
#endif
//student.cpp
#include"student.h"
using namespace std;
Student::Student() {
}
Student::Student(string n, char s, int num, double sco) {
name = n;
sex = s;
number = num;
score = sco;
}
double Student::get_average() {
return totalScore / totalAmount;
}
//对静态成员变量进行初始化
int Student::totalAmount = 0;
double Student::totalScore = 0;
//重载<<操作符,对Student类对象的格式化输出(格式:左对齐,姓名、性别、学号、成绩的域宽分别为10,6,15,8
ostream& operator<<(ostream& os, const Student& stu) {
os << setiosflags(ios::left) << setw(10) << stu.name << " " << setw(6) << stu.sex << " " << setw(15) << stu.number << " " << setw(8) << stu.score;
return os;
}
void student_info_parser(string file_name, vector<Student>& students) {
fstream fstrm;
Student stu;
fstrm.open("student_info.txt", ios::in);
if (!fstrm) cout << "Open Error" << endl;
while (fstrm >> stu.name) {
fstrm >> stu.sex;
fstrm >> stu.number;
fstrm >> stu.score;
students.push_back(stu);
stu.totalAmount++;
stu.totalScore += stu.score;
}
cout << "The number of student is " << stu.totalAmount << endl;
fstrm.close();
}
//main.cpp
#include"student.h"
using namespace std;
int main() {
Student stu;
vector<Student> students;
student_info_parser("student_info.txt", students);
for (int i = 0; i < students.size(); i++) {
cout << students[i] << endl;
}
cout << "The average score is " << stu.get_average() << endl;
return 0;
}
student_info.text文件
写这个题的时候,最后纠结的是把txt文件放在哪里,一开始放在了这
然后就得到了这个运行结果
显然不对
然后觉得应该跟源文件放在一起
最后放在了这里
终于对了
附上运行结果
小结
题目完成的很顺利,也基本完成了老师的要求,满分10分给自己打9分!
4.文件流
同样非常顺利的一个题,上课好好听,看看PPT就能写出来
题目描述:
设计一个用文件流方式完成下面操作:
生成一个 6 × 6 的矩阵 A,其元素为 [0,1] 之间的双精度数。
(a)将其按矩阵形式写入到一个文本文件 fout01.txt 中;
(b) 将其写入到一个二进制文件 fout01.dat 中;
(c) 再从文件 fout01.dat 中读取前 12 个数据(双精度),构成一个 2 × 6 的矩阵 B,并将 B 按行输出
直接附上代码
\\main.cpp
#include<iostream>
#include<fstream>
using namespace std;
double getRand() {
int number = rand() % 101;
return number / 100.0;
}
int main() {
//生成6*6个双精度数存入二维数组
double arr[6][6]={0};
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
arr[i][j] = getRand();
}
}
fstream fstrm;
fstrm.open("fout01.txt", ios::out);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
fstrm << arr[i][j] << " ";
}
fstrm << endl;
}
fstrm.close();
fstrm.open("fout01.dat", ios::out | ios::binary);
fstrm.write((char*)&arr, sizeof(arr));
fstrm.close();
double B[12];
fstrm.open("fout01.dat", ios::in | ios::binary);
fstrm.read((char*)&B, sizeof(B));
fstrm.close();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 6; j++) {
cout << B[6 * i + j] << " ";
}
cout << endl;
}
return 0;
}
中间出的小插曲有两个,一个是一开始定义的双精度二维数组没有赋值导致内存引起的错误,第二个是因为打字的问题文件名打的不一样导致的运行错误,最后改好就很OK
附上运行结果以及相关文件
小结
无比顺利,就是这样,满分10分给9.5分