面向对象程序设计课第三次作业

面向对象程序设计课第三次作业

更新于 2016/03/24

初始

此次的作业是要求实现一个计算器的数据读入和分类功能。
首先我跟着上一篇随笔中的教程学习了 C++ 的一些基本知识,包括 const 关键字,引用、类、对象的使用等等,然后利用 Dev-C 新建了一个 Caculator 项目,依据作业要求新增了 Scan 类和 Print 类,在 Scan 类下加入一个 ToStringQueue(string _input) 函数,但问题中所提到的使用 ,此前 没有听说过,就查了一下相关资料:一个类似栈的对象,先进先出,有 pop、push、front、back、empty、size 六个函数。初始化的语法是 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

原文地址:https://www.cnblogs.com/ladit/p/Assignment_3.html