C++小系统——餐馆员工管理系统及餐馆点菜系统(一)

C++ 专栏收录该内容
4 篇文章 0 订阅

    总算是把这个小系统给写完了,写的时间有点长(主要是中间过程遇到很多杂事,导致思路断断续续的,一直没怎么写)。

    这篇博客,我主要来讲,我在写的过程中遇到的错误,处理方法,以及部分细节。

    (源码及GitHub地址会在文末给出)

    1| 如何使用光标移动操作

        我就先贴这几段代码作为样例
//.h文件    
enum
    {
        UP = 72,
        DOWN = 80,
        LEFT = 75,
        RIGHT = 77,
        ENTER = 13,
        ESC = 27,
        BACKSPACE = 8,
        SPACE = 32,
        TAB = 9
    };
    //头文件代码里,我是选用枚举型,这样主要是便于观察,后期调试及键位增加的方便:-)
//.cc文件
int Employee::selectMenu(int Size ,int *thisIndex){
    int ch;
    ch = getch();
    switch(ch){
        case UP: if(*thisIndex>0) *thisIndex -= 2;break;
        case DOWN: if(*thisIndex<Size -2) *thisIndex += 2; break;
        case LEFT:if(*thisIndex>=10) *thisIndex -= 10;break;
        case RIGHT:if(*thisIndex<20) *thisIndex += 10;break;
        case SPACE:return SPACE;break;
        case ENTER:return ENTER;break;
        case ESC:return ESC;break;
        case TAB:return TAB;break;
    }
    return 0;
}

   

//.cc文件里展示函数的部分代码    
for(int i=0 ; i<Size ; i+=2){
        if(i == thisIndex){
            SetConsoleTextAttribute(hOut,FOREGROUND_RED|0x8);
            if(i>=0&&i<10){
                pos.X = 5;
                pos.Y = 5+i;
            }
            if(i >= 10&&i<20){
                pos.X = 35;
                pos.Y = i-5;
            }
            if(i>=20&&i<30){
                pos.X = 65;
                pos.Y = i-15;
            }
            SetConsoleCursorPosition(hOut,pos);
            cout<<"=>"<<types[i];
        }
        else{
            SetConsoleTextAttribute(hOut,FOREGROUND_BLUE|0x8);
            if(i>=0&&i<10){
                pos.X = 5;
                pos.Y = 5+i;
            }
            if(i >= 10&&i<20){
                pos.X = 35;
                pos.Y = i-5;
            }
            if(i>=20&&i<30){
                pos.X = 65;
                pos.Y = i-15;
            }
            SetConsoleCursorPosition(hOut,pos);
            cout<<types[i];
        }
    }
    fflush(stdout);
}

    呃~~感觉不放效果图不好解释(下面是最终效果图,我们先只看蓝色部分)


    强调一点,我这个蓝色部分的布局是用一维数组做+SetConsoleCursorPosition()  (API中的光标定位函数,用法见<SetConsolePosition()用法简析>)的,当然也可以用二维数组做,只不过二维数组用起来肯定没一维数组那么简单。

    好了,我们现在已经了解到用一维数组做,用SetConsoleCursorPosition()定好位之后,我们就该开始分析键盘的移动。

    (如果不用SetConsoleCursorPosition的话,一维数组输出基本上是处于一行)

    初始光标的数组下标为0时,也就是图中的可乐鸡翅的位置,它现在只有两个位置可以移动,即:向下,向右。

    向下: i+1

    向右: i+5

    (我们先暂且这样想,后面看到<SetConsolePosition()用法简析>你就知道为什么是”暂且“了)

    那如何判断当数组下标处于0时,不能往上或往左移呢?我们思路肯定都有,主要是如何具体实现这一操作,参考前面的第二段代码,如:

int Employee::selectMenu(int Size ,int *thisIndex){    
    int ch;
    ch = getch();
    switch(ch){
        case UP: if(*thisIndex>0) *thisIndex -= 2;break;

我先用getch()函数 和 整型变量ch ,来获取键盘键入的ASCII码值(UP已在枚举里赋值),当键入方向键“↑”时,便开始判断 *thisIndex (这是下标当前索引值,个人认为*thisIndex用的是比较好的,它直接修改所对应的内存空间的值,省去去了各个函数之间调用传值的麻烦),若*thisIndex值大于0,这代表它可以向右或向上动 ,然后对*thisIndex的值进行修改,展示函数通过循环,获取thisindex的值,便可出现效果。

  2| SetConsoleCursorPosition() & SetConsoleTextAttribute()

    考虑到后面需要很多这样的API函数来进行过渡介绍,暂时先介绍这两个,稍微了解一下就OK了,后面若是再出现其他的,我会直接在文中给出介绍,不再单独列出说明。

//需要头文件
#include <iostream>
#include <windows.h>

    # setConsoleCursorPosition() 设置光标打印位置

    1.设置句柄

    HANDLE hOut;                            //先声明一个句柄,基本上大部分跟打印的API有关系的都需要用到句柄
    hOut = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出句柄

    2.设置COORD

   在这里多说一句,COORD其实就是一个结构,里面含有X,Y的成员变量(注意是大写!!!) 

COORD pos = {0,0};    //这是初始位置,左上角开始

    3.设置位置坐标值

//我随便设置两个值

pos.X = 5;
pos.Y = 6;

//X,Y一定要大写!!!
//X,Y一定要大写!!!
//重要的事还是要凑出三遍
    4.SetConsoleCursorPosition()
SetConsoleCursorPosition(hOut,pos);    //按照这个格式写

    5.输出语句

cout << "你好" << endl;   //注意你想要输出的一定要在setconsolecursorposition()的后面

    # setConsoleTextAttribute() 设置文本的属性

    1.setConsoleTextAttribute()

    格式:setConsoleTextAttribute(句柄,颜色配置);

SetConsoleTextAttribute(hOut,FOREGROUND_GREEN|0x8); //举个例啦
    若想单独使用这个函数,那它前面只需上面介绍的API的第一个步骤就OK了,但是相同点是,仍要在输出语句前设置。

    颜色搭配可以参考这篇博客,这应该是最全的了吧~

    https://blog.csdn.net/qq_36642226/article/details/76690423


    2.5| 留个小空白给你

    若是你按照上面的做法去做,你会发现你的“菜单”的每行每列都紧紧的挨在一起了。

    那么,该如何解决这个问题呢呢?这个问题留给你自己思考,若是实在不会,你可以去文末的我留的github地址去clone一下我的文件,然后在menu.h头文件里,你就会找到答案啦。

   

   3| 关于状态栏及选菜栏的具体思路和做法

    思路:

    #状态栏:我需要在状态栏实现三个操作:

                      1.选菜数量输入

                      2.撤销上一次选菜操作

                      3.撤销提醒

    ##选菜栏:在选菜栏里,我需要实现两个操作:

                      1.选中后的菜名的显示及金额的显示

                      2.菜名的增加与消除

    ###选用的方法:

                      综合考虑前两个步骤,我决定在这里使用vector数组+结构体

                      1.选用vector数组的思想,在这里用vector去实现菜名的增加与当前金额的变化

                        相比较普通数组而言是很简单的。

                        再者,我已在前面说过菜单用一维数组解决,那vector是最方便和高效的

                       2.用结构体,是因为需要同时显示菜名及计算金额,单纯的数组只可以存储一种类型的值,

                         而在这里,结构体就可以帮上很大的忙。

    ####难点:

                       1.如何将vector数组+结构体结合使用。

    做法:

    思路现在是有了,关键就是在于如何去做。

    先看“添加”操作

    //这里是头文件里Employee类中的部分代码
    struct showStorage{
        std::string sName;
        int sCopies;
        int sPrice;
    };

    std::vector<showStorage> sStorage;
    我们先定义一个showStorage的结构体,里面成员变量有 sName(菜名), sCopies(数量), sPrice(单价)。 (其实这里的单价最好用double定义,当时只是为了完成这个系统,没考虑太多)

    然后用vector声明一个showStorage类型的变量sStorage。

    再来看看我们的操作机制,“SPACE”是确认键,“TAB”是撤销键。

    先看“SPACE”键:

        if(sele == SPACE){
            pos.X = 5;
            pos.Y = 17;
            SetConsoleCursorPosition(hOut,pos);
            switch(thisIndex){
                case 0:
                    inputValue(cou,thisIndex);
                    break;
                case 2:
                    inputValue(cou,thisIndex);
                    break;
                   ......
            }
            cou++;
        }

     这是SPACE的功能,先说明一点:这里的cou  是真的没有用。你可能会问:那你为什么要写啊 , 还把它加进函数里? 咳咳......这个......因为程序员的迷信...... 事情是这样的:我最后检查时,发现这里的cou没用,当时就一个个全部删除了,嗯?运行。 控制台一堆乱码。还好,我有备份。嗯。再来一次。乱码。?

    当时就开始慌了,删cou前,备份运行完美;删cou后,就炸了。。。没办法了,开始分析错误。

我知道可能数组越界,但不知道越界的那个数组在哪里?QAQ 找了好久,发现前面的某个地方的for循环里的判断条件多出来一个‘=’(问号脸.jpg)。。。然后,我就再不敢动程序里这根“龙脉”了。。。

    思维回来一下(正经.jpg),可以看到这里,我按下“SPACE”,switch结构会根据thisIndex的值进行判断。

    这里,我出现一个多余的写法,就是用switch,这里改为一个inputValue(cou,thisIndex);就好了。

    (源程序里面可能没改,我们知道就好了):)

    接下来看看inputValue函数的定义

void Employee::inputValue(int cou,int thisIndex){
    int wantNum;
    showStorage tag;//key step

    cout<<kind[thisIndex].name<<"× ";
    cin>>wantNum;

    tag.sName = kind[thisIndex].name;
    tag.sCopies = wantNum;
    tag.sPrice = kind[thisIndex].price * wantNum;

    sStorage.push_back(tag);

}

kind这个数组在menu.h里面

    struct storage{
        std::string name;
        int price;
    };

    storage kind[30]{
        {"可乐鸡翅",20},{},
        {"折耳根拌胡豆",20},{},
        {"糖醋排骨",20},{},
        {"皮蛋廋肉粥",20},{},
        {"鱼香肉丝",20},{},
        {"水煮鱼",20},{},
        {"鱼香茄子",25},{},
        {"红烧排骨",25},{},
        {"炸酱面",25},{},
        {"红烧鲤鱼",25},{},
        {"宫保鸡丁",25},{},
        {"麻婆豆腐",30},{},
        {"剁椒鱼头",30},{},
        {"豉香回锅肉",30},{},
        {"鲫鱼豆腐汤",30},{},
    };

可以看到这里 我们也是用结构体数组预先存储菜名,单价。

中间怎么有个“{ },”?哈哈,这就是2.5的答案啦!自己去试试吧!


对于inputValue()函数,我主要讲一下vector结构体的获取数值的方法,

               1.先定义一个对象:

                         可以看到我这里定义了一个showStorage类型的tag对象

               2.再用对象分别获取结构体里每个成员变量的值:

                         如:   tag.sName = kind[thisIndex].name;

               3.最后把这个tag对象加入vector的后面就可以了(这里考虑到“撤销”步骤的顺序性,

                 所以依次加入vector数组尾部):

                          sStorage.push_back(tag);

    到这里,“添加”步骤就基本完成啦,具体文本放的位置和方法,你照着上面的说明去操作就是了,这里就不详细说啦。

    接下来看“撤销”操作。

    “撤销”操作就比“添加”简单多了。估计我代码一放出来,你就明白了。

        if(sele == TAB){
            pos.X = 5;
            pos.Y = 17;
            SetConsoleCursorPosition(hOut,pos);
            char chO;
            cout<<"Do you want to delete your last choice?( Y / N )";
            cin>>chO;
            if(chO == 'Y'){
                sStorage.erase(sStorage.end()-1);    //看这里,重点是这一步
            }
        }

    直接erase就好了,什么?!具体erase用法?

    你慢慢看,我就不多解释了。http://www.cplusplus.com/reference/vector/vector/erase/

   4| “ENTER”键(提交)之后的需要注意的细节

    ENTER键详细的解说都已经放在代码里了

        if(sele == ENTER){
            system("cls");
            pos.X = 5;
            pos.Y = 13;
            SetConsoleCursorPosition(hOut,pos);
            char chT;
            cout<<"Do you want to submit your choice?( Y / N )"; //增加状态询问
            cin>>chT;
            if(chT == 'Y'){
                    system("cls");
                while(true){
                    SetConsoleTextAttribute(hOut,FOREGROUND_GREEN);
                    showAndPrintTheReceipt();
                    //initail the vector array;
                    sStorage.clear();       //<<<<<<<<<<<<为什么要再次把vector清空呢?
                    break;
                }
            }
        }

    我们再次思考一下代码中的问题。

    如果,我们每次用完vector后不清空数据的话,那么它的数据就会一直存在(当然前提是程序继续运行)。

    所以,我们必须要在每次“提交”后,对vector进行清空,以保证数据不会留存到下一次操作。

    若是不这样做,我感觉顾客会打死你。。。:D

   5|  由“餐馆收据”打印函数引发对TXT文本读写的深思

    我们既然聊到打印,就先看看原理。

    头文件  : #include <fstream>

    具体  :

             fstream();              //打开文件供读写

             ifstream()              //向已有文件读入

             ofstream()             //向文件写入

             常用打开模式:

                                  ios: :in                //只读

                                  ios: :out              //只写

                                  ios: :app             //从文末开始写

                                  ios: :trunc           //打开一个文件,清空内容再开始写

    下面我贴出基本格式

    //以输出文本为例
    ofstream outFile("file.txt",ios::trunc);
    if(!outFile.is_open()){
        exit(0);
    }
    else{
        ......
    }
    outFile.close();

    ofstream File ("文件名.txt" , 打开模式 );

    if(打开文件失败){

           //退出或其他操作

    }

    else{

           //你需要的操作

    }

    outFile.close();      //关闭文件

    //“读入文件”就把ofstream改为ifstream就好了。

    读写大致框架就是以上,当然还有其他的写法,这里不一 一罗列。

    下面看看读写细节。

    “写入”细节:

                         outFile<<你要写入的东西<<endl;

                         写入部分就和常规cout一样,若不加endl,就会导致文件全部写在一行,

                         这里可根据自己需求   进行处理。

    “读入”细节:

                        string Str , readStr;

                        ......

                        inFile>>readStr;

                        Str = readStr;

    这里的Str是用来被赋值的对象,我仅仅简写为Str(根据所处情况,灵活运用)。readStr,是用来获取读入的值。

    6| 关于数值与string字符串的相互转换

        头文件:#include<sstream>

        我基本上把这两个作为模板套进去了,可以做下笔记~~

//字符串转数值
int str2num(string s){
    int num;
    stringstream ss(s);
    ss>>num;
    return num;
}

//数值转字符串
string num2str(int i){
    stringstream ss;
    ss<<i;
    return ss.str();
}

~~~~(一)就先写到这里 ,最近忙于期末考试(斜眼笑.jpg),等有时间把(二)也写了。。。

 在(二)里面,我会写关于员工系统与餐馆管理系统的数据共用及修改。

okl,就先到这   :-)

github:https://github.com/Diomchen/THE(记得给个小星星哦~)




©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页

打赏作者

DiomChen

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值