面向对象程序设计课第三次作业
更新于 2016/03/24
初始
此次的作业是要求实现一个计算器的数据读入和分类功能。
首先我跟着上一篇随笔中的教程学习了 C++ 的一些基本知识,包括 const 关键字,引用、类、对象的使用等等,然后利用 Dev-C 新建了一个 Caculator 项目,依据作业要求新增了 Scan 类和 Print 类,在 Scan 类下加入一个 ToStringQueue(string _input)
函数,但问题中所提到的使用 queue<类型> 队列名
。
/******************************************************************************
FileName: Scan.h
Author:Ladit Version :1.1 Date:2016/03/24
Description: the scan class file,declare the scan class and function.
History:
<author> <time> <version> <description>
Ladit 16/03/24 1.1 add notes according to the regulations.
*****************************************************************************/
#ifndef SCAN_H
#define SCAN_H
#include <queue>
#include <string>
using namespace std;
class Scan
{
public:
Scan();
~Scan();
string ToStringQueue(string _input);
};
#endif
/******************************************************************************
FileName: Print.h
Author:Ladit Version :1.1 Date:2016/03/24
Description: the print class file,declare the print class and function.
History:
<author> <time> <version> <description>
Ladit 16/03/24 1.1 add notes according to the regulations.
*****************************************************************************/
#ifndef PRINT_H
#define PRINT_H
#include <queue>
#include <string>
using namespace std;
class Print
{
public:
Print();
~Print();
void getQueueAndOutput(queue<string>_queue);
};
#endif
至此都没有太大困难,这和上学期 C 语言学习中的 struct 类型十分相似。
Print 类较为简单,我在其下新建了一个 getQueueAndOutput(queue<string> _queue)
的函数,用以接收存储好的字符串队列并输出。实现的方法也较为简单,利用 cout 循环输出并 pop 队列即可。
输入部分在 main.cpp 中利用 cin 实现,在此不赘述。
难点
比较困难的是实现输入字符串的分类。我的想法是逐个字符读取,判断当前字符和下一个字符是否数字,若当前字符和下一个字符都是数字,则两者相加保存到一个字符串,若当前字符是数字而下一个字符不是数字,则返回当前保存的数字串,若当前字符不是数字,则直接返回当前字符。此外需要注意小数点也需要算作数字。利用两个 bool 变量即可完成判断和动作。此方法明白简单,但有一点是,在 main.cpp 中想要循环 push 进队列,就需要多次用到 ToStringQueue 这个函数,不能处理完后返回队列。因此每次处理完的字符串都必须去除掉已经处理过的字符串,所以我尝试在 main.cpp 中利用循环和暂存有效字符串后覆盖原字符串解决这个问题。然而经过多次尝试编译运行无法成功,出现了许多问题,如无法按期望输出字符、只输出了第一个字符、数字串最后一个数字被丢弃等。经过检查,我认为应该是 main 函数中的操作有误,而且 getQueueAndOutput(queue<string> _queue)
这个函数的逻辑存在问题。
解决
经过大神提醒,我发现即使是字符串队列,它的每一个元素的 size 只能是1,所以我想根据字符串队列的 front 元素的长度来循环删除主要字符串中已操作的字符是不可行的。上网查了一下发现 string 对象的确是神器,有一个 erase() 函数可以按不同格式删除字符串的某些内容。如指定位置和删除长度,或指定删除的位置。利用这个函数缩短了好几行代码。
getQueueAndOutput(queue<string> _queue)
函数中,我改为只判断当前字符是否数字/小数点,若是则加入待输出的字符串,若否则判断是否运算符号,若是则输出,若否则意味着此时是最后一个数字后的第一个字符,应当输出已存的数字字符串,此时还需要注意判断数字串是否超过10位。
/******************************************************************************
FileName: Scan.cpp
Author:Ladit Version :1.1 Date:2016/03/24
Description: the scan function file
Function List:
1. scan - scan the input string and return numbers or operators.
History:
<author> <time> <version> <description>
Ladit 16/03/24 1.1 add notes according to the regulations.
*****************************************************************************/
#include "Scan.h"
#include <string>
#include <queue>
using namespace std;
Scan::Scan() {}
Scan::~Scan() {}
string Scan::ToStringQueue(string _input)
{
bool Isnumber; //使用 bool 变量判断当前字符是否数字
string signstring = "",numstring = ""; //使用两个 string 变量存储需要输出的字符串
for(int i = 0; i < _input.size(); i++)
{
if ((_input[i] >= 48 && _input[i] <= 57) || _input[i] == '.')
{
Isnumber = true; //判断当前字符是否数字
}
else
{
Isnumber = false;
}
if (Isnumber) //若当前字符是数字则加入待输出的字符串
{
numstring += _input[i];
if (_input[i+1] == '\0')
{
return numstring;
}
continue;
}
else //否则输出当前非数字的字符或保存的字符串
{
if (numstring.empty())
{
signstring = _input[i];
return signstring;
}
else
{
if (numstring.size() > 10) //判断数字是否超过10位
{
return "ERROR";
}
else
{
return numstring;
}
}
}
}
}
/******************************************************************************
FileName: Print.cpp
Author:Ladit Version :1.1 Date:2016/03/24
Description: the print function file
Function List:
1. print - print the sorted numbers or operators queue.
History:
<author> <time> <version> <description>
Ladit 16/03/24 1.1 add notes according to the regulations.
*****************************************************************************/
#include "Print.h"
#include <iostream>
#include <string>
#include <queue>
using namespace std;
Print::Print() {}
Print::~Print() {}
void Print::getQueueAndOutput(queue<string> _queue)
{
while(_queue.size()) //当队列中有元素时循环输出
{
cout << _queue.front() << endl;
_queue.pop();
}
}
最终的 main 函数:
/******************************************************************************
FileName: main.cpp
Author:Ladit Version :1.1 Date:2016/03/24
Description: the main function file
Function List:
1. main - main function
History:
<author> <time> <version> <description>
Ladit 16/03/24 1.1 add notes according to the regulations.
*****************************************************************************/
#include "Scan.h"
#include "Print.h"
#include <iostream>
#include <string>
#include <queue>
using namespace std;
int main(int argc, char** argv)
{
Scan scan;
Print print;
string input;
cin >> input;
queue<string> que;
while (!input.empty()) //当剩余字符串不为空时进行循环
{
if (scan.ToStringQueue(input) == "ERROR") //若返回字符串为 ERROR 则报错并退出循环
{
cout << "ERROR:A number's length is longer than 10." << endl;
return 0;
}
que.push(scan.ToStringQueue(input)); //将返回的字符串 push 入队列
input.erase(0, scan.ToStringQueue(input).size()); //删除已处理过的字符串
}
print.getQueueAndOutput(que); //利用 Print 类中的函数输出队列
return 0;
}
在变量命名规则上,函数参数我倾向于在变量前加下划线,以便表明这是一个临时的函数参数,而实际参数用有意义的英文名称,简洁易懂。
此外,我遇到一些比较粗心的问题,例如忘记写 using namespace std
导致 cout、cin、endl 等无法使用;在 Scan.cpp 和 Print.cpp 中的函数不清楚格式而没写返回值类型,导致无法运行;各种临时变量,如 temp、numstring、signstring 忘记用完需要置空。这些都是一些基本的代码素养,以后需要注意。
BTW,要向请教过的各位大神表示感谢,例如橘子。
Github 链接
https://github.com/ladit/object-oriented/tree/master/Calculator