博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面向对象程序设计_Task5_Calculator1.5.0
阅读量:5105 次
发布时间:2019-06-13

本文共 8953 字,大约阅读时间需要 29 分钟。

The 3rd part of the Calculator program _ FILE I/O
题目链接:
github链接:

第三部分,开篇最想说的是,不知不觉进化到version1.5.0了(hhh~)

言归正传,这篇随笔有需要说明的一点是,由于在修改第四次作业的中途发布了第五次作业,所以这篇也会是上一次博客随笔的延续(评论中的下回分解hhh~)

So,在下文的代码前后,会结合第四、第五次作业的感受一起瞎掰。

以上。

Part 1

第五次作业代码方面的要求较之前相比,多了一个对文件读写的支持,且表达式自带等号。第二个问题很好解决,至于第一个,在对文件的读写有大致的了解之后也不是难题。

另外,用图形的方式描述整个项目的框架,即各个类之间的调用关系。

一脸懵逼.jpg

这是要用UML(Unified Modeling Language, 统一建模语言)的意思吗(貌似之前学JAVA过程中听过这个东西,类图用例图对象图各种图orz...)?

管他是不是,反正用了准没错= =,之后懵逼的去百度类图,还是没搞怎么明白,不过也大致画出了一点框架

下面,附上心得体会和代码实现...

Part 2

那么,依次对不同类进行分析,首先是:

  • Scan类

    在Scan类中,唯一的改变是ToStringQueue函数,针对第五次作业在表达式后自带等号的情况,所取得的表达式长度应为实际长度-1

    Scan.cpp

    queue
    Scan::ToStringQueue(string input) { queue
    q; // q即为最后所要return的队列,用于保存input表达式中的数字和符号 string res = ""; // res为扫描input表达式时用于临时存储的字符串 string strArr[N]; // strArr为扫描input表达式时用于存储数字和符号的string数组 int len = input.length() - 1; // 省略最后的等号'=' int cnt = 0; // 作为strArr的下标记录已完成提取的数字和符号的个数 for (int i = 0; i < len; i++) { if ((input[i] >= '0' && input[i] <= '9') || input[i] == '.') { res += input[i]; // 若是数字或 '.' 则添加到临时存储器res里, if (i == len - 1) // 若已完成扫描 { strArr[cnt++] = res; break; } continue; // 继续判断下一元素 } else { if (i == 0 && res.empty()) // 若第一个元素为‘-’ { res += input[i]; continue; } if (!res.empty()) // res非空表示此时res中缓存着一个数字 或 第一个元素为 '-' { strArr[cnt++] = res; } res = ""; // res置空 strArr[cnt++] = res + input[i]; // 将不是数字或 "." 的元素(即符号元素)添加到strArr中 } } for (int i =0; i < cnt; i++) // 将strArr中各个元素添加到q队列中 { if (strArr[i].length() > 10) { setIsExceed10(true); // 存在大于10位数的数字 } q.push(strArr[i]); } return q; }

接下来则是:

  • Calculation类

    这个类是有颇多的改动,其一是改变了栈中提取出来的操作数的类型,由int转为double,防止小数部分的截断以至于WA;

    其二则是对类成员的封装保护,将几个成员变量定义为private,并为其中一些变量提供了getter和setter方法;

    其三是在重复利用stringstream对象时,除了.clear(),还进行了.str("")操作;

    其四是将多个if-else语句简化为switch-case语句,优化、美观;

    最后,则是digitStack的改动,由stack换成了stack,处理上,简单的还真不是一星半点...orz...减少了stringstream流的使用频率,也缩减了代码量。

    calculation.h

    const int SIZE = 250;     // 表达式的最大长度(即q.size() <= SIZE)  class Calculation  {  public:      Calculation(void);      ~Calculation(void);      void setPriorityLevel();      stack
    & getDigitStack(); bool isOperator(string s); bool isDigit(string s); void toPostfixExpression(queue
    q); void calculatingExpression(queue
    q, bool is_Exceed10); private: int priority[128]; // 存储运算符的优先级 stack
    cacheStack; // 缓存栈 stack
    operatorStack; // 操作符栈,包括"+", "-", "*", "/", "(", ")" stack
    digitStack; // 数字栈,包括处理与运算过程 };

    Calculation.cpp

    void Calculation::calculatingExpression(queue
    q, bool is_Exceed10) { if (is_Exceed10) // 若超过10位数的数字存在为真 { cout << "Input error ! Not exceeding 10 digits expected! " << endl; return; } toPostfixExpression(q); // 将中缀表达式转换为后缀表达式 /**********计算部分***********/ string postfixExp[SIZE]; // 由于计算时需对缓存栈从栈底到栈顶的逐一扫描,故用string数组进行遍历操作 int expLen = 0; while (!cacheStack.empty()) { postfixExp[expLen++] = cacheStack.top(); cacheStack.pop(); } stringstream str_stream; // 用stringstream流进行string和double的格式转换 double res = 0; // res用于临时存储,push进数字栈 double rightNum = 0; // 右操作数 double leftNum = 0; // 左操作数 for (int i = expLen - 1; i >= 0; i--) { if (isDigit(postfixExp[i])) // 若该元素为数字,则直接入数字栈digitStack { str_stream << postfixExp[i]; // 格式转换:string->double str_stream >> res; digitStack.push(res); str_stream.clear(); // stringstream流的清空,以便重复利用 str_stream.str(""); } else if (isOperator(postfixExp[i])) // 若该元素为运算符,则弹出数字栈的两个数进行相应的运算并将结果push进数字栈 { if (!digitStack.empty()) { rightNum = digitStack.top(); digitStack.pop(); } if (!digitStack.empty()) { leftNum = digitStack.top(); digitStack.pop(); } switch (postfixExp[i][0]) // 对应加法、减法、乘法、除法运算 { case '+': digitStack.push(leftNum + rightNum); break; case '-': digitStack.push(leftNum - rightNum); break; case '*': digitStack.push(leftNum * rightNum); break; case '/': digitStack.push(leftNum / rightNum); break; default: break; } } } }

最后的就是第五次作业的主体:

  • Print类

    ######作业要求:所有关于输出的代码,都写在Print类里面

    相较之前,这次几乎将所有的输出都放在了Print类,而且这次作业的一个很大的改进是,终于想到在类的构造函数和析构函数里面写点东西了→_→

    而且,在一段纠结是否要重载PrintResult函数的时间之后,还是选用了不同的输出函数处理,更便于理解(理解万岁~)

    这里还有要说到的就是文件的读写,我用的是fstream文件流来操作,(),用file_read和file_write关联两个文件,循环从输入文件开头读到文件尾,一次读取一个表达式并将表达式的值输出到输出文件内。

    print.h

    class Print  {  public:      Print(void);      ~Print(void);      void printError();      void printResult(string input);      void printResult_a(string input);      void printResult_f(string inputAddr, string outputAddr);      void printQueue(queue
    q, bool isExceed10); private: Scan *sc; Calculation *ca; };

    print.cpp

    #include "Print.h"  #include 
    #include
    Print::Print(void) // 在构造函数中初始化 *sc 和 *ca { sc = new Scan(); ca = new Calculation(); } Print::~Print(void) // 在析构函数中释放 { delete sc; sc = NULL; delete ca; ca = NULL; } void Print::printError() { cout << "Input error!" << endl; } /************************************************* Description: 针对命令行参数为 表达式 的输出 Input: string input: 待计算的表达式 Output: 表达式的值 *************************************************/ void Print::printResult(string input) { ca->calculatingExpression(sc->ToStringQueue(input), sc->getIsExceed10()); cout << ca->getDigitStack().top() << endl; ca->getDigitStack().pop(); } /************************************************* Description: 针对命令行参数为 "-a"和表达式 的输出 Input: string input: 待计算的表达式 Output: 表达式及表达式的值 *************************************************/ void Print::printResult_a(string input) { cout << input << " "; ca->calculatingExpression(sc->ToStringQueue(input), sc->getIsExceed10()); cout << ca->getDigitStack().top() << endl; ca->getDigitStack().pop(); } /************************************************* Description: 针对命令行参数为 "-f"和输入、输出文件地址 的输出 Input: string inputAddr: 即读取的文件地址 string outputAddr: 即写入的文件地址 *************************************************/ void Print::printResult_f(string inputAddr, string outputAddr) { string input; fstream file_read(inputAddr); fstream file_write(outputAddr, ios::out); while (!file_read.eof()) { file_read >> input; ca->calculatingExpression(sc->ToStringQueue(input), sc->getIsExceed10()); file_write << input << " " << ca->getDigitStack().top() << "\n"; ca->getDigitStack().pop(); } file_read.close(); file_write.close(); }

Part 3

下面则是main函数的实战部分,由于将输出部分移到了Print类,故main函数只需对传进的命令行参数进行判断即可(依旧用了strcmp...),针对不同输入格式调用Print类的不同方法。

main.cpp

#include "Print.h"#include 
#include
using namespace std;/************************************************* Description: 接收表达式的输入并求值 Input: int argc: 命令行参数个数 char *argv[] : 实际传入的命令行参数 Return: 若正常结束,则返回0*************************************************/int main(int argc, char *argv[]){ Print *pr = new Print(); if (argc == 2) // 对传入的参数只有表达式的处理,调用Print类对应的PrintResult方法输出 { pr->printResult(argv[1]); } else if (!strcmp(argv[1], "-a") && argc == 3) // 对传入的参数为"-a"的处理,调用Print类对应的PrintResult_a方法输出 { pr->printResult_a(argv[2]); } else if (!strcmp(argv[1], "-f") && argc == 4) // 对传入的参数为"-f"的处理,调用Print类对应的PrintResult_f方法输出 { pr->printResult_f(argv[2], argv[3]); } else { pr->printError(); }// system("pause"); return 0;}

然后附上实战结果= =

Task5_test

Part 4

最后,则是框架图...模仿着加上几个类和清一色的几个箭头----------→→→→欧啦= =

Task5_框架图

Part 5

胡乱讲了一大堆导致篇幅有点长,感觉自己的代码还是有很多提升的空间的,比如Scan类的ToStringQueue方法可以改进,而且初始化也是一个需要注意的点,再加上对构造、析构函数的运用,函数的重载等等。

期待指点,期待vision 1.6.0的到来(hhh~)

参考资料:

The End

转载于:https://www.cnblogs.com/monsterJang/p/5479487.html

你可能感兴趣的文章
Vue_(组件通讯)子组件向父组件传值
查看>>
jvm参数
查看>>
我对前端MVC的理解
查看>>
Silverlight实用窍门系列:19.Silverlight调用webservice上传多个文件【附带源码实例】...
查看>>
2016.3.31考试心得
查看>>
mmap和MappedByteBuffer
查看>>
Linux的基本操作
查看>>
转-求解最大连续子数组的算法
查看>>
对数器的使用
查看>>
OracleOraDb11g_home1TNSListener服务启动后停止,某些服务在未由其他服务或程序使用时将自己主动停止...
查看>>
Redis用户添加、分页、登录、注册、加关注案例
查看>>
练习2
查看>>
【ASP.NET】演绎GridView基本操作事件
查看>>
ubuntu无法解析主机错误与解决的方法
查看>>
尚学堂Java面试题整理
查看>>
MySQL表的四种分区类型
查看>>
[BZOJ 3489] A simple rmq problem 【可持久化树套树】
查看>>
STM32单片机使用注意事项
查看>>
swing入门教程
查看>>
好莱坞十大导演排名及其代表作,你看过多少?
查看>>