实验目的
1. 掌握类的设计、定义、实现和测试
2. 掌握C++程序以项目文件组织的多文件结构编写形式
3. 深度理解面向对象编程与结构化编程在编程解决实际问题时思维方式的不同
实验准备
1. C++程序以项目文件方式组织的多文件结构 浏览学习教材「5.6节 多文件结构和编译预处理指令」
2. 类和对象 类的设计、定义(尤其注意构造函数及其初始化列表书写形式) 对象的定义、访问
实验内容
Part1 验证性内容:以多文件结构组织的项目文件示例:在画布上可以上下左右移动的小球
选做*
画布类Canvas和球类Ball成员函数的实现中有不少重复的代码,思考如何优化,使得代码更简洁,同时保持程序的可读性和逻辑清晰性
Part2 基于已有信息,补足并扩充程序
选做*
扩展类Graph的功能,使其:
支持重新设置字符、尺寸,每次重新设置后,自动重绘图形
支持图形的前景色、背景色设置和调整 支持通过按键控制图形水平移动或垂直移动,等等
Part3 基于需求描述设计、定义并实现分数类Fraction,并编写代码完成测试
具体要求如下: 设计一个分数类 Fraction描述分数(两个整数的比值)
设计并实现接口,实现以下要求:
分数类Fraction的基本功能列表 定义Fraction类对象时,支持如下形式:
Fraction a; // 默认没有提供任何初始化数据时,分数对象的默认值为0/1
Fraction b(3,4); // 定义分数对象时,如果提供两个初始化参数,代表分数3/4
Fraction b(5); // 定义分数对象时,如果提供两个初始化参数,代表分数5/1
Fraction类对象能够进行如下操作:
加、减、乘、除运算
对两个分数值进行比较运算,返回其大小关系
分数的输入、输出
选做*:
类Fraction的扩展功能
对分数进行规范化处理,确保分数在形式上,只有分字为负数,并且,分数值是约简形式,即:
2/-3 经过规范化处理后,转换为 -2/3
15/21 经过规范化处理后,转换为5/7
-2/-6 经过规范化处理后,转换为1/3
实验结论
//不会截gif图
1.小球游戏?//只简化了代码,关于移动的部分在实验二做了,又没想到能有什么游戏就没写
#include <iostream> #include "canvas.h" #include "Ball.h" int main() { Canvas canvas; Ball ball1(10,10); system("pause"); ball1.left(5); system("pause"); ball1.up(20); system("pause"); canvas.changeCanvasFg("E"); // 更新画布前景色 system("pause"); canvas.changeCanvasBg("D"); // 更新画布背景色 system("pause"); return 0; }
#include "ball.h" #include <iostream> #include <cstdlib> // 因为使用了system("cls"); 所以需要包含这个头文件 using std::cout; using std::endl; const int SIZE_X=50; // 小球x轴移动范围0~SIZE_X const int SIZE_Y=50; // 小球y轴移动范围0~SIZE_Y //移动 void Ball::move() { // 打印y0-1行空行 for (int line = 1; line <= y- 1; line++) cout << endl; // 打印x0-1个空格 for (int col = 1; col <= x - 1; col++) cout << " "; // 打印小球 cout << "O" << endl; } Ball::Ball(int x0, int y0):x(x0), y(y0) { move(); } void Ball::left(int step) { x = x-step; if(x <= 0) x=0; // 清屏 system("cls"); move(); } void Ball::right(int step) { x = x+step; if(x >= SIZE_X) x=SIZE_X; // 清屏 system("cls"); move(); } void Ball::up(int step) { y = y-step; if(y <= 0) y=0; // 清屏 system("cls"); move(); } void Ball::down(int step) { y = y+step; if(y >= SIZE_Y) y = SIZE_Y; // 清屏 system("cls"); move(); }
#ifndef CANVAS_H #define CANVAS #include <string> using std::string; class Canvas { public: Canvas(string bg0="0", string fg0="A"); void changeCanvasBg(string bg0); void changeCanvasFg(string fg0); void changeCanvasColor(string bg0, string fg0); void change();//改变颜色 private: string bg; // background color string fg; // foreground color }; #endif
#include "canvas.h" #include <cstdlib> //改变颜色 void Canvas::change() { string color = "color "; color += bg; color += fg; system(color.c_str()); } Canvas::Canvas(string bg0, string fg0):bg(bg0), fg(fg0) { change(); } void Canvas::changeCanvasBg(string bg0) { bg = bg0; // 更新画布背景色 change(); } void Canvas::changeCanvasFg(string fg0) { fg = fg0; // 更新画布前景色 change(); } void Canvas::changeCanvasColor(string bg0, string fg0){ bg = bg0; // 更新画布背景色 fg = fg0; // 更新画布前景色 change(); }
#include <iostream> #include "canvas.h" #include "Ball.h" int main() { Canvas canvas; Ball ball1(10,10); system("pause"); ball1.left(5); system("pause"); ball1.up(20); system("pause"); canvas.changeCanvasFg("E"); // 更新画布前景色 system("pause"); canvas.changeCanvasBg("D"); // 更新画布背景色 system("pause"); return 0; }
结论:
2.绘图?
#ifndef GRAPH_H #define GRAPH_H // 类Graph的声明 class Graph { public: Graph(char ch='?', int n=1,int nx=0,int ny=0); // 带有参数的构造函数 void draw(); // 绘制图形 void change(char ch, int n);//修改参数 void left();//左 void right();//又 void up();//上 void down();//下 private: int x; int y; char symbol; int size; }; #endif
// 类graph的实现 #include "graph.h" #include <iostream> #include<iomanip> #include<string> using namespace std; // 带参数的构造函数的实现 Graph::Graph(char ch/*='?'*/, int n/*=1*/,int nx/*=0*/,int ny/*=0*/): symbol(ch), size(n),x(nx),y(ny) { } // 成员函数draw()的实现 // 功能:绘制size行,显示字符为symbol的指定图形样式 void Graph::draw() { string str,an; str += symbol; an += symbol; an += symbol; int k,l; if (y != 0) { for (l = 1; l <= y; l++) cout << endl; } for (k = 1; k <= size; k++) { if (k > 1) str += an; cout << setw(size + k +x- 1) << str << endl; } } //改变参数 void Graph::change(char ch, int n){ symbol = ch; size=n; } //左 void Graph::left() { if (x > 0) x--; } //右 void Graph::right() { x++; } //上 void Graph::up() { if (y > 0) y--; } //下 void Graph::down() { y++; }
#ifndef CANVAS_H #define CANVAS #include <string> using std::string; class Canvas { public: Canvas(string bg0 = "0", string fg0 = "A"); void changeCanvasBg(string bg0); void changeCanvasFg(string fg0); void changeCanvasColor(string bg0, string fg0); void change();//改变颜色 private: string bg; // background color string fg; // foreground color }; #endif
#include "canvas2.h" #include <cstdlib> //改变颜色 void Canvas::change() { string color = "color "; color += bg; color += fg; system(color.c_str()); } Canvas::Canvas(string bg0, string fg0) :bg(bg0), fg(fg0) { change(); } void Canvas::changeCanvasBg(string bg0) { bg = bg0; // 更新画布背景色 change(); } void Canvas::changeCanvasFg(string fg0) { fg = fg0; // 更新画布前景色 change(); } void Canvas::changeCanvasColor(string bg0, string fg0) { bg = bg0; // 更新画布背景色 fg = fg0; // 更新画布前景色 change(); }
#include <iostream> #include<string> #include<conio.h> #include "graph.h" #include"canvas2.h" using namespace std; //刷新 void putout(Graph x) { system("cls"); cout << "1.输入符号和大小" << endl; cout << "2.更改背景色" << endl; cout << "3.更改前景色" << endl; cout << "4.清屏" << endl; cout << "5.移动(wsad上下左右,q退出移动)" << endl; x.draw(); } int main() { //初始化测试 cout << "测试" << endl; Graph graph1('*',5); graph1.draw(); system("pause"); system("cls"); Graph graph2('$',7); graph2.draw(); system("pause"); system("cls"); //结束测试 char ch; int n; Graph g; Canvas v; string nbg, nfg; char mov; int jud=1; cout << "1.输入符号和大小" << endl; cout << "2.更改背景色" << endl; cout << "3.更改前景色" << endl; cout << "4.清屏" << endl; cout << "5.移动(wsad上下左右,q退出移动)" << endl; cout << "初始图形" << endl; g.draw(); while (cin >> jud) { switch (jud) { //改变符号和大小 case 1: cin >> ch >> n; g.change(ch, n); putout(g); continue; //改变背景色 case 2: cin >> nbg; v.changeCanvasBg(nbg); putout(g); continue; //改变前景色 case 3: cin >> nfg; v.changeCanvasFg(nfg); putout(g); continue; //清屏 case 4: system("cls"); cout << "1.输入符号和大小" << endl; cout << "2.更改背景色" << endl; cout << "3.更改前景色" << endl; cout << "4.清屏" << endl; cout << "5.移动(wsad上下左右,q退出移动)" << endl; continue; //移动 case 5: for (;;) { mov = _getch(); //退出 if (mov == 'q') { putout(g); break; } else { switch (mov) { //上 case 'w': g.up(); putout(g); continue; //下 case 's': g.down(); putout(g); continue; //左 case 'a': g.left(); putout(g); continue; //右 case 'd': g.right(); putout(g); continue; } } } } } system("pause"); return 0; }
结论://多图警告
3.分数类//不会运算符重载所以代码很繁杂
//输出时自动规范化
#ifndef FRACTION_H #define FRACTION_H //Fraction类的声明 class Fraction{ public: Fraction(int ntop=0,int nbo=1); Fraction(Fraction &a); void in(int x, int y);//输入 void tong(Fraction &a, Fraction &b);//通分 void yue();//约分/规范化 void putout();//输出 void add( Fraction a, Fraction b);//加 void minus( Fraction a, Fraction b);//减 void multiply( Fraction a, Fraction b);//乘 void divide( Fraction a, Fraction b);//除 void compare( Fraction x, Fraction y);//比大小 private: int top;//分子 int bottom;//分母 }; #endif
#include"Fraction.h" #include<iostream> using namespace std; //求最大公因数(不含零)(取模) int common(int x, int y) { if (x < 0) x = -x; if (y < 0) y = -y; int max, min = x, a; if (y < x) min = y; for (a = 1; a <= min; a++) if (x%a == 0 && y%a == 0) max = a; return max; } //构造函数 Fraction::Fraction(int ntop /*= 0*/, int nbo /*= 1*/):top(ntop),bottom(nbo){} //复制构造函数 Fraction::Fraction(Fraction &a):top(a.top),bottom(a.bottom){} //输入 void Fraction::in(int x, int y) { top = x; bottom = y; } //所有最大公因数均是取模后得出的结果 //通分 void Fraction::tong(Fraction &a, Fraction &b) { int com = common(a.bottom, b.bottom); int al = a.bottom / com, bl = b.bottom/ com; a.bottom *= bl; b.bottom *= al ; a.top *= bl; b.top *= al; } //约分/范化 void Fraction::yue() { if (top == 0) bottom = 1; else { int com = common(top, bottom); top /= com; bottom /= com; if (bottom < 0) { bottom *= -1; top *= -1; } } } //输出 void Fraction::putout() { cout << top << '/' << bottom; } //加法 void Fraction::add( Fraction a,Fraction b) { if (a.top != 0 && b.top != 0) { tong(a, b); top = a.top + b.top; bottom = a.bottom; } else if (a.top == 0) { top = b.top; bottom = b.bottom; } else { top = a.top; bottom = a.bottom; } yue(); } //减法 void Fraction::minus(Fraction a, Fraction b) { if (a.top != 0 && b.top != 0) { tong(a, b); top = a.top - b.top; bottom = a.bottom; } else if (a.top == 0) { top = -b.top; bottom = b.bottom; } else { top = a.top; bottom = a.bottom; } yue(); } //乘法 void Fraction::multiply( Fraction a, Fraction b) { top = a.top*b.top; bottom = a.bottom*b.bottom; yue(); } //除法 void Fraction::divide( Fraction a, Fraction b) { top = a.top*b.bottom; bottom = a.bottom*b.top; yue(); } //比大小 void Fraction::compare( Fraction x, Fraction y) { Fraction a=x,b=y; tong(a, b); if (a.bottom < 0) { a.top *= -1; b.top *= -1; } if (a.top > b.top) { x.putout(); cout<<'>'; y.putout(); } if (a.top ==b.top) { x.putout(); cout << '='; y.putout(); } if (a.top < b.top) { x.putout(); cout << '<'; y.putout(); } }
#include<cstdlib> #include"Fraction.h" #include<iostream> #include<string> using namespace std; //输入式子 //还有不少重复的代码没有简化 char putin(Fraction &a,Fraction &b,char *str) { int t1=0, t2=0, b1=0, b2=0; int length = strlen(str), m=0; char re; int jud=1;//判断正负用 //除法 if (str[m] == '(') { re = '/'; m += 1; if (str[m]== '-') {//判断正负 jud = 0; m += 1; } for (m ; m < length; m++) { if (str[m] != '/'&&str[m] != ')') t1 = 10 * t1 + (str[m] - '0'); else break; } if (jud == 0) {//处理正负 t1 = -t1; jud = 1; } if (str[m] == ')') a.in(t1, 1);//用一个数为分数1赋值 else { m += 1; if (str[m] == '-') {//判断正负 jud = 0; m += 1; } for (m ; m < length; m++) { if (str[m] != ')') b1 = 10 * b1 + (str[m] - '0'); else break; } if (jud == 0) {//处理正负 b1 = -b1; jud = 1; } a.in(t1, b1);//用两个数为分数1赋值 } m += 3; if (str[m] == '-') {//判断正负 jud = 0; m += 1; } for (m ; m < length; m++) { if (str[m] != '/'&&str[m] != ')') t2 = 10 * t2 + (str[m] - '0'); else break; } if (jud == 0) {//处理正负 t2= -t2; jud = 1; } if (str[m] == ')') b.in(t2, 1);//用一个数为分数2赋值 else { m += 1; if (str[m]== '-') {//判断正负 jud = 0; m += 1; } for (m; m < length; m++) { if (str[m] != ')') b2 = 10 * b2+ (str[m] - '0'); else break; } if (jud == 0) {//处理正负 b2 = -b2; jud = 1; } b.in(t2, b2);//用两个数为分数2赋值 } } //加减乘和比较 else { if (str[m] == '-') {//判断正负 m += 1; jud = 0; } for (m ; m < length; m++) { if (str[m]!='/'&&str[m]!='+'&&str[m] != '-'&&str[m] != 'C'&&str[m] != '*') t1 = 10 * t1 + (str[m] - '0'); else break; } if (jud == 0) {//处理正负 t1 = -t1; jud = 1; } if (str[m] != '/') { re = str[m]; a.in(t1, 1);//用一个数为分数1赋值 } else { m += 1; if (str[m] == '-') {//判断正负 jud = 0; m += 1; } for (m ; m < length; m++) { if (str[m] != '+'&&str[m] != '-'&&str[m] != 'C'&&str[m] != '*') b1 = 10 * b1 + (str[m] - '0'); else break; } if (jud == 0) {//处理正负 b1 = -b1; jud = 1; } re = str[m]; a.in(t1, b1);//用两个数为分数1赋值 } m += 1; if (str[m] == '-') {//判断正负 jud = 0; m += 1; } for (m ; m < length; m++) { if (str[m] != '/') t2 = 10 * t2 + (str[m] - '0'); else break; } if (jud == 0) {//处理正负 t2 = -t2; jud = 1; } if (str[m] != '/') b.in(t2, 1);//用一个数为分数2赋值 else { m += 1; if (str[m] == '-') {//判断正负 jud = 0; m += 1; } for (m ; m < length; m++) b2 = 10 * b2+ (str[m] - '0'); if (jud == 0) {//处理正负 b2 = -b2; jud = 1; } b.in(t2, b2);//用两个数为分数2赋值 } } return re; } //主函数 int main(){ //测试初始化 cout << "测试初始化" << endl; Fraction a(2), b, c(-2,5),d=c; a.putout(); cout << " "; b.putout(); cout << " "; c.putout(); cout << " "; d.putout(); cout << " "<<endl; //计算 cout << "输入公式,按下回车开始计算" << endl; cout << "(C代表比较两分数大小,进行除法时用括号标注分数)" << endl; Fraction x, y,en; char str[30],jud; while (cin >> str) { jud = putin(x, y, str); switch (jud) { case '+'://加 en.add(x, y); cout << "="; en.putout(); cout << endl; continue; case '-'://减 en.minus(x, y); cout << "="; en.putout(); cout << endl; continue; case '*'://乘 en.multiply(x, y); cout << "="; en.putout(); cout << endl; continue; case '/'://除 en.divide(x, y); cout << "="; en.putout(); cout << endl; continue; case 'C'://比大小 en.compare(x, y); cout << endl; continue; } } system("pause"); return 0; }
结论:
实验结论
part1:可以利用函数来简化代码,要想完成一个完整的游戏还需要很多工作。
part2:本来应该单独用一个类控制移动的,我本来想用更快速的方法画图,但效果不太好。
还有一个bug——更改字符时必须用不同的字符,我也没有找出错误的原因。希望指正
part3:用运算符重载应该会好很多,我本应该创建一个类来输入的。还有代码还有很多重复的可以精简。
以上是出现的问题。