总算是把这个小系统给写完了,写的时间有点长(主要是中间过程遇到很多杂事,导致思路断断续续的,一直没怎么写)。
这篇博客,我主要来讲,我在写的过程中遇到的错误,处理方法,以及部分细节。
(源码及GitHub地址会在文末给出)
1| 如何使用光标移动操作
我就先贴这几段代码作为样例
呃~~感觉不放效果图不好解释(下面是最终效果图,我们先只看蓝色部分)
强调一点,我这个蓝色部分的布局是用一维数组做+SetConsoleCursorPosition() (API中的光标定位函数,用法见<SetConsolePosition()用法简析>)的,当然也可以用二维数组做,只不过二维数组用起来肯定没一维数组那么简单。
好了,我们现在已经了解到用一维数组做,用SetConsoleCursorPosition()定好位之后,我们就该开始分析键盘的移动。
(如果不用SetConsoleCursorPosition的话,一维数组输出基本上是处于一行)
初始光标的数组下标为0时,也就是图中的可乐鸡翅的位置,它现在只有两个位置可以移动,即:向下,向右。
向下: i+1
(我们先暂且这样想,后面看到<SetConsolePosition()用法简析>你就知道为什么是”暂且“了)
那如何判断当数组下标处于0时,不能往上或往左移呢?我们思路肯定都有,主要是如何具体实现这一操作,参考前面的第二段代码,如:
我先用getch()函数 和 整型变量ch ,来获取键盘键入的ASCII码值(UP已在枚举里赋值),当键入方向键“↑”时,便开始判断 *thisIndex (这是下标当前索引值,个人认为*thisIndex用的是比较好的,它直接修改所对应的内存空间的值,省去去了各个函数之间调用传值的麻烦),若*thisIndex值大于0,这代表它可以向右或向上动 ,然后对*thisIndex的值进行修改,展示函数通过循环,获取thisindex的值,便可出现效果。
2| SetConsoleCursorPosition() & SetConsoleTextAttribute()
考虑到后面需要很多这样的API函数来进行过渡介绍,暂时先介绍这两个,稍微了解一下就OK了,后面若是再出现其他的,我会直接在文中给出介绍,不再单独列出说明。
# setConsoleCursorPosition() 设置光标打印位置
1.设置句柄
2.设置COORD
在这里多说一句,COORD其实就是一个结构,里面含有X,Y的成员变量(注意是大写!!!)
3.设置位置坐标值
4.SetConsoleCursorPosition()5.输出语句
# setConsoleTextAttribute() 设置文本的属性
1.setConsoleTextAttribute()
格式:setConsoleTextAttribute(句柄,颜色配置);
颜色搭配可以参考这篇博客,这应该是最全的了吧~
https://blog.csdn.net/qq_36642226/article/details/76690423
2.5| 留个小空白给你
若是你按照上面的做法去做,你会发现你的“菜单”的每行每列都紧紧的挨在一起了。
那么,该如何解决这个问题呢呢?这个问题留给你自己思考,若是实在不会,你可以去文末的我留的github地址去clone一下我的文件,然后在menu.h头文件里,你就会找到答案啦。
思路:
#状态栏:我需要在状态栏实现三个操作:
1.选菜数量输入
2.撤销上一次选菜操作
3.撤销提醒
##选菜栏:在选菜栏里,我需要实现两个操作:
1.选中后的菜名的显示及金额的显示
2.菜名的增加与消除
###选用的方法:
综合考虑前两个步骤,我决定在这里使用vector数组+结构体
1.选用vector数组的思想,在这里用vector去实现菜名的增加与当前金额的变化
相比较普通数组而言是很简单的。
再者,我已在前面说过菜单用一维数组解决,那vector是最方便和高效的
2.用结构体,是因为需要同时显示菜名及计算金额,单纯的数组只可以存储一种类型的值,
而在这里,结构体就可以帮上很大的忙。
####难点:
1.如何将vector数组+结构体结合使用。
做法:
思路现在是有了,关键就是在于如何去做。
先看“添加”操作
然后用vector声明一个showStorage类型的变量sStorage。
再来看看我们的操作机制,“SPACE”是确认键,“TAB”是撤销键。
先看“SPACE”键:
这是SPACE的功能,先说明一点:这里的cou 是真的没有用。你可能会问:那你为什么要写啊 , 还把它加进函数里? 咳咳......这个......因为程序员的迷信...... 事情是这样的:我最后检查时,发现这里的cou没用,当时就一个个全部删除了,嗯?运行。 控制台一堆乱码。还好,我有备份。嗯。再来一次。乱码。?
当时就开始慌了,删cou前,备份运行完美;删cou后,就炸了。。。没办法了,开始分析错误。
我知道可能数组越界,但不知道越界的那个数组在哪里?QAQ 找了好久,发现前面的某个地方的for循环里的判断条件多出来一个‘=’(问号脸.jpg)。。。然后,我就再不敢动程序里这根“龙脉”了。。。
思维回来一下(正经.jpg),可以看到这里,我按下“SPACE”,switch结构会根据thisIndex的值进行判断。
这里,我出现一个多余的写法,就是用switch,这里改为一个inputValue(cou,thisIndex);就好了。
(源程序里面可能没改,我们知道就好了):)
接下来看看inputValue函数的定义
kind这个数组在menu.h里面
可以看到这里 我们也是用结构体数组预先存储菜名,单价。
中间怎么有个“{ },”?哈哈,这就是2.5的答案啦!自己去试试吧!
对于inputValue()函数,我主要讲一下vector结构体的获取数值的方法,
1.先定义一个对象:
可以看到我这里定义了一个showStorage类型的tag对象
2.再用对象分别获取结构体里每个成员变量的值:
如: tag.sName = kind[thisIndex].name;
3.最后把这个tag对象加入vector的后面就可以了(这里考虑到“撤销”步骤的顺序性,
所以依次加入vector数组尾部):
sStorage.push_back(tag);
到这里,“添加”步骤就基本完成啦,具体文本放的位置和方法,你照着上面的说明去操作就是了,这里就不详细说啦。
接下来看“撤销”操作。
“撤销”操作就比“添加”简单多了。估计我代码一放出来,你就明白了。
直接erase就好了,什么?!具体erase用法?
你慢慢看,我就不多解释了。http://www.cplusplus.com/reference/vector/vector/erase/
ENTER键详细的解说都已经放在代码里了
我们再次思考一下代码中的问题。
如果,我们每次用完vector后不清空数据的话,那么它的数据就会一直存在(当然前提是程序继续运行)。
所以,我们必须要在每次“提交”后,对vector进行清空,以保证数据不会留存到下一次操作。
若是不这样做,我感觉顾客会打死你。。。:D
我们既然聊到打印,就先看看原理。
头文件 : #include <fstream>
具体 :
fstream(); //打开文件供读写
ifstream() //向已有文件读入
ofstream() //向文件写入
常用打开模式:
ios: :in //只读
ios: :out //只写
ios: :app //从文末开始写
ios: :trunc //打开一个文件,清空内容再开始写
下面我贴出基本格式
ofstream File ("文件名.txt" , 打开模式 );
if(打开文件失败){
//退出或其他操作
}
else{
//你需要的操作
}
outFile.close(); //关闭文件
//“读入文件”就把ofstream改为ifstream就好了。
读写大致框架就是以上,当然还有其他的写法,这里不一 一罗列。
下面看看读写细节。
“写入”细节:
outFile<<你要写入的东西<<endl;
写入部分就和常规cout一样,若不加endl,就会导致文件全部写在一行,
这里可根据自己需求 进行处理。
“读入”细节:
string Str , readStr;
......
inFile>>readStr;
Str = readStr;
这里的Str是用来被赋值的对象,我仅仅简写为Str(根据所处情况,灵活运用)。readStr,是用来获取读入的值。
头文件:#include<sstream>
我基本上把这两个作为模板套进去了,可以做下笔记~~
~~~~(一)就先写到这里 ,最近忙于期末考试(斜眼笑.jpg),等有时间把(二)也写了。。。
在(二)里面,我会写关于员工系统与餐馆管理系统的数据共用及修改。
okl,就先到这 :-)
github:https://github.com/Diomchen/THE(记得给个小星星哦~)