您的位置:首页 > 编程语言 > C语言/C++

程序设计第三次作业---C++计算器雏形

2016-03-19 13:59 686 查看
Github链接:https://github.com/Wasdns/object-oriented

题目:程序设计第三次作业

程序设计第三次作业附加

我的程序设计第三次作业附加 代码规范

更新时间:2016/3/23

第一次尝试:(此时题目尚未更改,我按要求把
-
和数字连起来输出)

我在学习完相关的C++类的知识还有STL中stack和queue的用法之后,大概捋清了题意。用不到十五分钟的时间敲出了代码1的大体,但还是由于自己对相关知识的不熟悉,修改大大小小的错误花费了我一天的时间,代码1如下。

代码1
.cpp
:

#include<stdlib.h>
#include<iostream>
#include<string>
#include<queue>
#include<cstdio>
using namespace std;
class Scan
{
// private section
public:
void ToStringQueue(string input);
};
void Scan::ToStringQueue(string input)
{
string s;//Define a string "s",and store(存储) something by using it.
queue<string>que;//Define a queue "que".
int m,n,i,j,t=0;
n=input.size();//Define int n,"n = string's length".
for(i=0;i<n;i++)//mistake:i=1;i<=n;
{
if(input[i]=='-')
{
t=1;

que.push(s);//
s.clear();//clear s

s+=input[i];
continue;
}
else
{
if(t==1)// '-'
{
if(input[i]=='('||input[i]==')'||input[i]=='+'||input[i]=='*'||input[i]=='/')
{
t=0;

que.push(s);
s.clear();

s+=input[i];

que.push(s);//que.push(input[i]);
s.clear();
}
else
{
s+=input[i];
continue;
}
}
else
{
if(input[i]=='('||input[i]==')'||input[i]=='+'||input[i]=='*'||input[i]=='/')
{
que.push(s);
s.clear();

s+=input[i];

que.push(s);//que.push(s);
s.clear();
}
else
{
s+=input[i];
continue;
}
}
}
}

m=s.size();
if(m!=0)que.push(s);//check s
s.clear();

while(que.empty()==0)//mistake:j<n;
{
cout<<que.front()<<endl;
que.pop();
}
}

int main()
{
int n=0,i,j;
Scan sc;//实例化对象miss
string b;
cin>>b;
sc.ToStringQueue(b);
return 0;
}
//example:
//-100+(98-97)*2
//-100+(98+97)*2
//100*(99-98)
//100*10+1000
//100*10-100*(98*97-1)+100
//


输入样例:
100*10-100*(98*97-1)+100


运行结果:



思路分析:

大概来阐述一下我的思路,在
void Scan::ToStringQueue(string input)
接收完队列input以后,首先定义一个空字符串s和一个类型为
queue<string>
的队列que(定义语句:
queue<string>que;
),其次利用string的函数
input.size()
把字符串input的长度(一共有几个字符)赋值给int类型的n,再利用for循环对一个个字符进行判断。

我还定义一个int类型的变量t:
int t=0;
,并且利用t的值进行状态判断。这里使用bool变量进行判断也是可以的。

如果此时for循环处理的字符
input.[i]
-
(这里利用
==
判断两个字符是否相同),把t的值转换成1。而语句
que.push(s);s.clear();
是把s之前储存的内容push到队列que,再利用函数
s.clear()
清除字符串里的字符。然后把
-
存储到字符串s里:
s+=input[i];
(这里我犯了一个错误),continue。然后对下一个元素进行判断,如果是数字再利用+=存储到字符串s,利用continue,一直到下一个字符串input的元素不是数字为止,比如
-100+
,s存储到
-100
的时候,把s的内容push到队列里,再清空字符串s,对下一个元素进行判断。如果不是数字,而是
+
,
/
,
*
,
(
,
)
这样的符号的话,和上面同理,先把s先前存储的内容push,清空s,再利用+=把此时判断的字符存储到字符串s上,再push到队列que,清空s。

简单来说,如果遇到
-
,把t的值转为1,再看下一个元素,如果下一个元素是数字再进行存储的操作;如果是其他字符就把s存储的先前内容push,清空s,再把s现在要存储的东西push,清空s,把t的值转为0。

在for循环结束后,有如下代码:

m=s.size();
if(m!=0)que.push(s);//check s
s.clear();


利用函数
s.size()
把此时字符串的长度赋值给int类型的m,判断m是否为0,如果不为0,说明s仍有存储的内容尚未push,把剩下的内容push并清空s。

根据上面的测试结果,读者会发现,输出结果多了几个行,而且输出并没有按照要求放在一个类里面。

为什么会多输出
\n
(换行符)呢,这个问题我们先放一放。

关于第二个问题,代码1是我的最开始的测试代码,它用于验证我的大体思路和想法是否正确,与最后的成品相比亘长且不成熟。

在代码1的编写过程中我遇到了大大小小的问题,下面即是我对这些问题的一个总结和反思。

问题1:
for(i=1;i<=n;i++)

这个问题跟我之前给数组元素赋值有关,如果有n个变量需要我输入,我喜欢从a[1]开始(假如定义了一个数组a),这给我带来了方便,但是也是一个坏习惯。

而此处,我输入一个字符串input,比如是
-100+99
-
就是由input.[0]存储的。

我在发现这个问题以后把i=1改成了i=0,但是i<=n忘记更改了,于是输出结果的后面莫名多了个a。



最后发现它的时候我也是醉了...马虎过头。

解决方法:
for(i=0;i<n;i++);


问题2:
que.push(input[i]);

这是我之前的push语句,编译并没有通过,主要原因是队列que存储的是string类型,定义que的语句:
queue<string>que;
,而
input[i]
是字符类型,与函数push传递的变量类型string不相符。

解决方法:自定义一个空字符串s,来储存字符
input.[i]


问题3:主函数里未实例化对象。

这是C++类的知识,在这个.cpp文件里我定义了一个Scan类,并且类外定义了函数:
void ToStringQueue(string input)
,这与
::
运算符有关。

而在主函数中如果想要调用Scan类中声明的函数ToStringQueue,就必须实例化一个Scan类的对象。

参考:实例化对象

解决方法:在主函数里面定义一个对象sc:
Scan sc;
然后通过调用函数ToStringQueue:

sc.ToStringQueue(input)


关于"::"运算符

第二次尝试(
-
单独一行):

在第一次尝试的铺垫和理解下,第二次尝试我试着把class的声明内容放到.h文件内,而其他定义内容则在.cpp文件内进行。

代码2:

.h


#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
/*
* No description
*/
class Print
{
public:
void pout();
};
class Scan
{
// private section
public:
void ToStringQueue(string input);
};

#endif // CALCULATOR_H


.cpp
:

#include "calculator.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
queue<string>que;
string s;
void Scan::ToStringQueue(string input)
{
int n=input.length(),m=0,i,j;//n是字符串input的长度
for(i=0;i<n;i++)//n has no define;
{
if(input[i]=='-'||input[i]=='+'||input[i]=='('||input[i]==')'||input[i]=='/'||input[i]=='*')
{
que.push(s);
s.clear();

s+=input[i];

que.push(s);
s.clear();
}
else
{
s+=input[i];
continue;
}
}
m=s.size();//利用m判断s是否为空串
if(m!=0)que.push(s);
s.clear();
}

void Print::pout()//输出函数pout单独声明在out类里
{
while(que.empty()==0)
{
cout<<que.front()<<endl;
que.pop();
}
}
int main()
{
Scan Sc;//实例化对象Sc
Print Put;//实例化对象Put
string input;
cin>>input;
Sc.ToStringQueue(input);
Put.pout();
return 0;
}
//example:-100+(98-97)*2


输入样例:
-100+(98-97)*2


运行结果:



思路分析:

.cpp
文件与代码1类似,我这里仍然利用一个空字符串s来存储input的元素。不同的是,由于题目做出了改变,我这里并没有对
-
进行判断。

而代码2比较特别的地方在于它的
.h
文件,我把对于Scan和Print类的声明放在了
.h
文件里,那么
.h
文件里就是声明类的部分,
.cpp
文件里是定义函数及其他的部分。

注意,我创建的
.h
.cpp
文件都是在同一个工程里(工程1),在
.cpp
文件的开头需加上:
#include"calculator";
来包括
calculator.h
这个头文件。

问题:

因为在这之前有了代码1的实战体验,敲代码2比较快,而且错误少。在刚开始我忘记对n进行赋值(但是对它进行了初始化:
n=0;
),导致队列que内并没有push进东西:



没有输出说明循环出了问题,很快我就找到了n并解决了这个问题:
n=input.length();


到目前为止,我把代码的大体已经成功的写出来了,现在仍然还有等待我解决的问题和要求:

要求:

1)当输入的数字超过10位(包括小数位)时,报错。

2) 能够处理不超过10位的任意实数。

3) 代码要有注释,变量名要有意义。

4) 遵循编码规范(编码规范参考),可读性尽可能好。

以下是我根据要求做出的改进:

代码3:

.h
文件:


#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;

/*
* No description
*/

class Print
{
public:
void pout();
};

class Scan
{
// private section
public:
void ToStringQueue(string input);
};

#endif // CALCULATOR_H

.cpp
文件:


#include "calculator.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;

queue<string>que; // Define a <string>queue "que";
string s; // Define string s;用空串s存储字符
int t=0; // Define t=0; 来计算位数

// Using global variables here.

void Scan::ToStringQueue(string input)
{
int n=input.length(),m=0,i,j;
for(i=0;i<n;i++) // Error: n has no define;
{
if(t>10) // such as "10,000,000,000"
{
cout<<"Error"<<endl;
break;
}

if(input[i]=='-' || input[i]=='+' || input[i]=='(' || input[i]==')' || input[i]=='/' || input[i]=='*')
{
t=0; // stop the count

que.push(s);
s=""; // 相当于s.clear();

s+=input[i];

que.push(s);
s="";
}
else
{
t++;
s+=input[i];
continue;
}
}

if(t<=10) // ifnormal
{
m=s.size();
if(m!=0)que.push(s);
s="";
}
}

void Print::pout()
{
if(t<=10) // ifnormal
{
while(que.empty()==0) // "que" is not empty
{
cout<<que.front()<<endl;
que.pop();
}
}

}

int main()
{
Scan Sc; // Define a "Scan" object:"Sc".
Print Put; // Define a "Print" object "Put".
string input;

cin>>input;

Sc.ToStringQueue(input);
Put.pout();
return 0;
}
//-100+(98-97)*2


代码3我使用的是定义全局变量的方法,这样在
void Scan::ToStringQueue(string input)
里把应该存储到队列que的值push到que内,再在
void Print::pout
中输出队列que存储的内容。

这样是比较方便,但是全局变量有其短缺和不利的地方。那么就有我改进它的理由。

第三次尝试:

“调用Scan对象的ToStringQueue(string input),将输入传进去以获取队列。接着将这个队列传入Print对象的方法中,得到输出。”

代码4:

.h
文件:


#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;

/*
* No description
*/

class Print
{
public:
void pout(queue<string>que);//type:queue<string>
};

class Scan
{
// private section
public:
queue<string>ToStringQueue(string input);//type:queue<string>
};

#endif // CALCULATOR_H

.cpp
文件:


#include "calculator.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;

int t=0;//Using global variable "t";

queue<string>Scan::ToStringQueue(string input)//type:queue<string>
{
int n=input.length(),m=0,i,j;
queue<string>que;
string s;
for(i=0;i<n;i++) // Error: n has no define;
{
if(t>10) // such as "10,000,000,000"
{
cout<<"Error"<<endl;
break;
}

if(input[i]=='-' || input[i]=='+' || input[i]=='(' || input[i]==')' || input[i]=='/' || input[i]=='*')
{
t=0; // stop the count

que.push(s);
s=""; // s.clear();

s+=input[i];

que.push(s);
s="";
}
else
{
t++;
s+=input[i];
continue;
}
}

if(t<=10) // ifnormal
{
m=s.size();
if(m!=0)que.push(s);
s="";
}
return que;
}

void Print::pout(queue<string>que)
{
if(t<=10) // ifnormal
{
while(que.empty()==0) // "que" is not empty
{
cout<<que.front()<<endl;
que.pop();
}
}

}

int main()
{
queue<string>que;//type:queue<string>
Scan Sc; // Define a "Scan" object:"Sc".
Print Put; // Define a "Print" object "Put".
string input;

cin>>input;

que=Sc.ToStringQueue(input);
Put.pout(que);
return 0;
}
//-100+(98-97)*2

问题探究:

这次与前两次不同的地方在于函数ToStringQueue的类型:
queue<string>
在调用该函数之后,函数返回存储字符串的队列que,再传入到函数pout。

这里我也是栽了几次,首先最开始时我是这样定义的:
queue ToSringQueue<string input>
,编译器很明显告诉我没有
queue
这个类型,不让我通过。

在定义存储string类型的时候我使用了
queue<string>que;
这样的语句,类比于之前定义整形变量的
int a;
的“类型+变量名”,我想
queue<string>
会不会就是我想要的答案呢?经过编译器的肯定,证明了我的想法是正确的。

解决方法:
queue<string>Scan::ToStringQueue(string input)


样例测试:

样例:
-100+(98-97)*2


运行结果:



在尝试过程中,测试样例无一是令我满意的,原因我之前也有提到,就是多输出了空行。

那么问题出在哪里呢,我首先怀疑的是输入字符串之后我敲的一个回车,但我很快发现这并不影响。于是就把问题锁定在了队列queue上了,输出有空行,说明在入队的时候出现了情况。

测试:

样例1:
-100+(98-97)*2


输出结果:



样例2:
10000000000000-10


输出结果:



样例3:
10+(999-10000000000*2)-10*1


输出结果:



由样例2和样例3可以验证“当输入的数字超过10位(包括小数位)时报错”这一部分是正确的。

经过比较长时间的寻找,我得到了答案:

原来的代码片段:

t=0; // stop the count

que.push(s);
s=""; // s.clear();

s+=input[i];

que.push(s);
s="";


在我停止计数以后(即遇到了除数字之外的其他字符),队列que进行了
que.push(s)
操作,倘若之前s存储了内容(比如
100
),这当然是正确的。那么如果s是空串,传入队列que的也是空字符串,也就造成了输出结果存在空行的情况。

解决方法:

t=0; // stop the count

if(s!="")
que.push(s);
s=""; // s.clear();

s+=input[i];

que.push(s);
s="";


测试:

样例1:
-100+(98-97)*2


输出结果:



样例2:
100*10-100*(98*97-1)+100


输出结果:



可以看到,成功解决了输出之间夹空行的问题。

最终的代码:

.h
.文件:

#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
/*
* No description
*/

class Input
{
public:
string Get();
};

class Print
{
public:
void pout(queue<string>que);//type:queue<string>
};

class Scan
{
// private section
public:
queue<string>ToStringQueue(string input);//type:queue<string>
};

#endif // CALCULATOR_H

.cpp
文件

#include "calculator2.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;

int t=0;//Using global variable "t";

string Input::Get()
{
string s;
cin>>s;
return s;
}

queue<string>Scan::ToStringQueue(string input)//type:queue<string>
{
int n=input.length(),m=0,i,j;
queue<string>que;
string s;
for(i=0;i<n;i++) // Error: n has no define;
{
if(t>10) // such as "10,000,000,000"
{
cout<<"Error"<<endl;
break;
}

if(input[i]=='-' || input[i]=='+' || input[i]=='(' || input[i]==')' || input[i]=='/' || input[i]=='*')
{
t=0; // stop the count

if(s!="")//avoid null string
que.push(s);
s=""; // s.clear();

s+=input[i];

que.push(s);
s="";
}
else
{
t++;
s+=input[i];
continue;
}
}

que.push(s);
s.clear();

/*if(t<=10) // ifnormal
{
m=s.size();
if(m!=0)que.push(s);
s="";
} */

return que;
}

void Print::pout(queue<string>que)
{
if(t<=10) // ifnormal
{
while(que.empty()==0) // "que" is not empty
{
cout<<que.front()<<endl;
que.pop();
}
}
}

int main()
{
queue<string>que;//type:queue<string>
Scan Sc; // Define a "Scan" object:"Sc".
Print Put; // Define a "Print" object "Put".
Input Ge;// Define a "Input" object "Ge".
string input;

input=Ge.Get();//receieve input.
que=Sc.ToStringQueue(input);//receieve que.

Put.pout(que);//printf que.
return 0;
}
//-100+(98-97)*2


最后我把输入也放在单独的一个class
Input
里面了。

小结:在我做完这篇随笔的时候,寒假已经接近尾声了,回顾这个不短不长的寒假,我或多或少收获了一些东西。拿这篇随笔来说,我动笔的时候我还不清楚说在这次作业里需要用到queue和string的一些功能,但现在也算是了解了它。当时容易犯下的诸如忘记在main函数里实例化类的对象这样的错误,现在已经很少见了。这次作业让我第一次对项目有所了解和感触,完成它的期间我参阅了一些对我帮助颇深的资料,也有很多贵人相助,感激之余也对下学期的学习生活充满了遐想,自己通过下学期的学习会达到一个怎样的高度?我所面对的挑战和压力(像这次作业)我能不能很好的解决它?像这次的作业,从发布时的一无所知,到翻阅资料初有了解,再到通过不断的实践完成它,我想,这虽不足以回答我的问题,但是就像是一个引领的坐标,使我有了走下去的动力和勇气。



2016/2/22


附:本文参考资料及书籍:

1.面向对象程序设计-C++网易公开课 授课人:翁恺

2.C++远征封装篇 授课人:james_yuan

3.Git教程 廖雪峰的官方网站

4.《挑战程序设计竞赛》第二版

感谢
@Sxiaopeng
@chs97
朋友的帮助和指导


附:git操作:

git config --global user.name"Wasdns"

git config --global user.email"952693358@qq.com"

git init(可以省略,因为之前对
object-oriented
已经执行了该命令)

git add calculator

git commit -m'calculator'

git push -u origin master

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: