你好,C++(30)“大事化小,小事化了”5.4.3 工资程序成长记:函数

5.4.3 工资程序成长记:函数

自从上次小陈“程序员”的工资程序得到老板的夸奖,口头许诺给他涨工资以后,老板再也没有找过他,涨工资的事自然也就没有下文了。这天,老板又突然召他去办公室。这下可把小陈高兴坏了,心想盼星星盼月亮终于盼来涨工资这一天了。于是赶紧到了老板的办公室。可他刚进门就发现情况有点不对,只见老板阴沉着脸坐在他那张硕大的老板椅上,满头大汗,手指还在不停地敲击着键盘输入着什么。一见到小陈进来,就好像见到仇人似的,劈头盖脸地来了一句:

“小陈啊,你这个工资程序怎么搞的,怎么每次都要重新输入工资数据啊?你是不是想累死我啊?”

老板的话如同一个晴天霹雳,让小陈的好心情一下子从山顶跌落到了谷底,心想,这下涨工资的事情肯定泡汤了。听老板这一抱怨,他才想起来原来的工资程序没法读取已有的工资数据,也无法将已经输入的工资数据保存成文件以备下次使用。所以这个工资程序是“一次性”的,每次使用都要重新输入全部数据。小陈想想那成千上万的工资数据,心中暗想,这都没把这个可恶的老板累死,真是算他走运了。不过转念又想,要是把他累死了谁给我涨工资啊?于是,赶紧解释说:

“老板,这可能是工资程序存在的一个缺陷,也就是我们通常所说的Bug(臭虫)。您也是知道的,程序中出现Bug是在所难免的。让我拿回去修改修改,马上就好,马上就好。”

就这样,小陈没有听到涨工资的好消息,反倒是拿了一个有问题的工资程序回来修改。好在他这几周还算勤快,在C++方面进步了不少,他已经学到了函数,懂得了如何对一个问题“自顶向下、逐步求精”地进行分析,划分模块并用C++的函数加以解决。所以,对于解决老板遇到的这个问题,他还是很有信心的。

拿到问题后,小陈对这个问题进行了简单的分析。整体而言,这是一个典型的对数据进行处理的程序,按照业务流程,它主要可以分为数据输入、数据处理和数据输出三个比较大的模块。而根据输入方式的不同,数据输入模块又可以细分成手工输入和文件读入,而数据输出部分,除了屏幕显示结果之外,相应地还应该包括将工资数据输出到文件。在最重要的数据处理模块,为了保证函数的职责单一而明确,需要将原来混杂在数据输入时进行的最大值最小值以及平均值的计算,移到数据处理模块中成为独立的子模块。经过这样的简单分析,小陈很快地在白板上画出了工资程序的模块图:

图5-11 工资程序模块图

有了程序的模块图,我们只要用函数将每个模块实现,然后在主函数中按照业务流程对各个子模块加以调用,就可以解决整个问题了。在模块图的指引下,小陈很快就用函数实现了每个模块,并将它们组装成了新的工资程序:

// 工资程序V2.0
#include <iostream>
// 为了读写文件,引入文件流对象头文件
#include <fstream>
#include <string>
#include <climits> // 为了使用int类型的最大值和最小值

using namespace std; 

// 全局的工资数据文件名,使用一个不可修改的常量字符串表示
const string strFileName = "SalaryData.txt";

// 从数据文件读取工资数据到arrSalary数组
int Read(int* arrSalary, int nCount)
{
    int i = 0;   // 当前工资序号

    // 打开工资数据文件SalaryData.txt用于读入数据
    // 这个文件应该在exe文件所在的相同目录下
    ifstream in(strFileName);

    if(in.is_open()) // 如果成功打开数据文件
    {
         // 构造一个while循环读取文件中的工资数据
         // 如果读取的数据个数i小于数组的容量nCount,则继续读取
        while(i < nCount)
        {
             // 将读取的数据保存到arrSalary[i]
             in>>arrSalary[i];
             // 对读取结果进行判断,看是否读取到了文件结束
             // 如果到达文件结尾,则用break关键字结束读取循环
             if(!(in))
             {
                 break;
              }

             ++i;  // 尚未读取完毕,开始下一次读取
        }

        // 读取完毕,关闭文件
         in.close();
    }
    // 输出读取结果,返回读取的数据个数
    cout<<"读取"<<i<<"个工资数据"<<endl;
    return i;
}

// 将arrSalary数组中的工资数据写入数据文件
void Write(int* arrSalary, int nCount)
{
    // 创建或打开工资数据文件SalaryData.txt用于输出
    // 输出完成后,这个文件将出现在exe文件所在的目录
    ofstream o(strFileName);
    if(o.is_open()) // 如果成功打开数据文件
    {
        // 利用for循环,将数组中的数据输出到文件
         for(int i = 0;i < nCount;++i)
        {
             o<<arrSalary[i]<<endl; // 每一行一个数据
        }

        // 输出完毕,关闭文件
        o.close();
    }
}

// 获取工资数组中的最大值
int GetMax(int* arrSalary, int nCount)
{
    int nMax = INT_MIN; // 初始值为int类型的最小值
    // 利用for循环遍历数组中所有数据元素,逐个进行比较
    for(int i = 0;i < nCount; ++i)
    {
        if(arrSalary[i] > nMax)
             nMax = arrSalary[i];
    }
    // 返回找到的最大值
    return nMax;
}

// 获取数组中的最小值(请依最大值函数的葫芦自行画出最小值函数的瓢)…

// 计算数组中所有数据的平均值
float GetAver(int* arrSalary, int nCount)
{
    // 计算总和
    int nTotal = 0;
    for(int i = 0;i < nCount; ++i)
    {
        nTotal += arrSalary[i];
    }

    // 计算平均值并返回
    if(0 != nCount) // 判断总数是否为0
        return (float)nTotal/nCount;
    else
        return 0.0f; // 特殊情况返回0
}

// 手工输入数据
int Input(int* arrSalary, // 工资数组首地址
       int nMax,    // 数组能够容纳的数据的个数      //
       int nIndex)  // 数组中已有的数据的个数
{
    // 用数组中已有数据的个数作为输入的起点
    int i = nIndex;  // 在for循环之前初始化i,应为i在for循环之后还需要用到
    for(; i < nMax; ++i) // i已经初始化,初始化语句留空
    {
        // 提示输入
        cout<<"请输入"<<i<<"号员工的工资(-1表示输入结束):"<<endl;
        // 将输入的数据保存到数组的arrSalary[i]数据元素
        int n = 0;
        cin>>n;
        // 检查输入是否合法
         if(cin)
        {
             arrSalary[i] = n;
        }
        else// 如果输入不合法,例如输入了英文字符,则提示用户重新输入
        {
             cout<<"输入错误,请重新输入"<<endl;
             // 清理cin的输入标志位以重新输入
             cin.clear();
             // 清空输入缓冲区
             cin.sync();
             --i;  // 将输入序号退后一个
             continue; // 直接开始下一次循环
        }

        // 检查是否输入结束
        if(-1 == arrSalary[i])
        {
             break; // 结束输入循环
        }
    }

    // 返回当前数组中共有的数据个数
    return i;
}

// 查询工资数据
void Find(int* arrSalary,int nCount)
{
    while(true)  // 构造无限循环进行工资查询
    {
        int n = 0;
        // 提示用户输入要查询的员工序号
        cout<<"请输入要查询的员工序号(0-"<<nCount-1
             <<",-1表示结束查询):"<<endl;
        // 获取用户输入的员工序号并保存到n
        cin>>n;

        // 对用户输入进行检查
        if(!cin) // 如果用户输入不合法
        {
             cout<<"输入错误,请重新输入"<<endl;
             // 清理cin的输入标志位以重新输入
             cin.clear();
             // 清空输入缓冲区
             cin.sync();
             continue; // 开始下一次查询
        }
        else if(-1 == n) // 检查查询是否结束
        {
             // 查询结束,用break结束循环
             cout<<"查询完毕,感谢使用!"<<endl;
             break;
        }
        else if(n<0||n>=nCount) // 检查输入是否超出序号范围
        {
             // 输入序号超出范围,用continue开始下一次循环
             cout<<"输入的序号"<<n<<"超出了序号范围0-"
              <<nCount-1<<",请重新输入。"<<endl;
             // 开始下一次查询
             continue;
        }

        // 输入合法,输出用户查询的员工工资
        cout<<"员工序号:"<<n<<endl;
        cout<<"员工工资:"<<arrSalary[n]<<endl;
    }
}

int main()
{
    // 定义保存员工数据的超大数组
    const int MAX = 100000;
    int arrSalary[MAX] = {0};

    // 首先从数据文件读取已经保存的数据
    int nCount = Read(arrSalary,MAX);
    // 然后用手工继续输入工资数据
    nCount = Input(arrSalary,MAX,nCount);

    // 对输入的工资数据进行统计
    cout<<"输入完毕。一共有"<<nCount<<"个工资数据"<<endl;
    cout<<"最大值:"<<GetMax(arrSalary,nCount)<<endl;
    cout<<"最小值:"<<GetMin(arrSalary,nCount)<<endl;
    cout<<"平均值:"<<GetAver(arrSalary,nCount)<<endl;

    // 对工资数据进行查询
    Find(arrSalary,nCount);

    // 查询结束,将工资数据保存到数据文件,以备下次使用
    Write(arrSalary,nCount);

    return 0;
}

在改写的过程中,小陈按照面向过程中“自顶向下,逐步求精”的设计思路,首先将整个问题分解成了手工输入、文件输入、获取最大值等多个模块,然后利用多个函数分别将这些模块一一实现,最后在主函数中将这些函数按照业务流程组织起来,最终解决了整个大问题。当小陈将这个改写后的工资程序拿给老板用过之后,老板笑得合不拢嘴,一直夸奖说:

“不错不错,现在的工资程序不仅可以将输入的数据保存下来,下次可以接着用,再也不用我每次都重新输入数据了。同时还对用户的输入进行了检查,很好地防止了输入错误的发生。干的不错,下个月,涨工资,啊哈哈哈……”

老板的一句“涨工资”,让小陈又重新燃起了希望,心中想,这次的改写,让我见识了函数所体现出来的这种“将一个大问题分解成多个小问题,然后各个击破”的解决问题的思路,以后遇到再大的问题也不用担心了。同时他也暗下决心,C++真是个好东西,只要自己好好学,下个月一定能够涨工资。

时间: 2024-10-27 18:00:59

你好,C++(30)“大事化小,小事化了”5.4.3 工资程序成长记:函数的相关文章

(转载)你好,C++(23) 4.4.2 工资程序成长记:用数组处理批量数据,用循环结构执行重复动作

你好,C++(23) 4.4.2 工资程序成长记:用数组处理批量数据,用循环结构执行重复动作 4.4  从语句到程序 了解了各种表达式和语句之后,就相当于掌握了写作文要用到的词语和句子,但是,仅有词语和句子是无法构成一篇有意义的文章的.要完成一篇文章,先需要确定这篇文章的结构,是先分述再总述,还是逐层递进论述.定好结构后再按照结构的要求将词语和句子安排到合适的位置,这样才能写出一篇有意义的词句通顺的文章.编写程序就像写文章一样,也同样需要先根据需要处理的事务确定程序的流程控制结构,然后再将那些零

你好,C++(23) 4.4.2 工资程序成长记:用数组处理批量数据,用循环结构执行重复动作

4.4  从语句到程序 了解了各种表达式和语句之后,就相当于掌握了写作文要用到的词语和句子,但是,仅有词语和句子是无法构成一篇有意义的文章的.要完成一篇文章,先需要确定这篇文章的结构,是先分述再总述,还是逐层递进论述.定好结构后再按照结构的要求将词语和句子安排到合适的位置,这样才能写出一篇有意义的词句通顺的文章.编写程序就像写文章一样,也同样需要先根据需要处理的事务确定程序的流程控制结构,然后再将那些零散的语句串联起来描述一个完整的处理事务的过程,从而将多条零散的语句组织成可以完成一定功能的完整

.NET软件汉化小实例

Author:KillerLegend Date:2014.6.18 From:http://www.cnblogs.com/killerlegend/p/3795577.html 好的,今天我们来汉化一个小软件,其功能是修改Win7的登录界面背景的. 解压后,先用PEID侦测文件,如下图: 可以看到这个软件没有加壳,编写语言是C#/.NET. 先运行一下程序,看一看界面,如下图: 我么需要汉化的东西不是很多,所以我们主要的目的是找到汉化的思路. 接下来出场的是Reflector,我用的是8.2

钉钉用户过亿,但马云得小心马化腾的反杀

恭喜陈航!1075天的努力,终于实现了1个亿的小目标.在企业级服务市场上,国内终于做出了一款用户过亿的产品,这也是一件可喜可贺的事情. 接下来,我们中小企业的办公效率将会有一个较大的提升,企业级服务市场或迎来新一轮的爆发. 但我要说的是,钉钉用户过亿,跟QQ.微信关系真的不大. 一.钉用户过亿,马化腾该紧张吗? 钉钉用户过亿,网上不少声音说社交格局要变天了,事实如此吗?马化腾会因为钉钉用户过亿紧张吗? 何玺想说的是,钉钉用户过亿,跟生活社交并没有多大的关系,马化腾也不会因为这个紧张QQ.微信的市

30岁创业,30岁女人小本创业项目推荐

30岁,有一定的经验.人脉,是适合创业的年龄.30岁创业,有哪些适合的项目?30岁女人小本创业项目推荐! 30岁女人小本创业项目?开家低热量零食店 零食,这似乎是女性和小孩的专利.据广州食品与健康协会调查,有90.8%的女性都有吃零食的习惯.但是零食热量高,容易让人发胖,因此,低热量零食应运而生,弥补了普通零食的一大缺陷,且市场需求量逐渐增长.目前,上海.广州.宁波等城市有选择低热量食品店这个投资少创业致富之道赚钱的. 低热量食品主打健康.方便路线,以多元化零食为优势,甚至有“带餐”的作用.小店

眼见为实(2):介绍Windows的窗口、消息、子类化和超类化

眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较"生僻"的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和线程.子类化(Subclassing)和超类化(Superclassing)是伴随Windows窗口机制而产生的两个复用代码的方法.不要把"子类化.超类化"与面向对象语言中的派生类.基类混淆起来."子类化.超类化"中的"类"是指W

Android组件化和插件化开发

http://www.cnblogs.com/android-blogs/p/5703355.html 什么是组件化和插件化? 组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Module),开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件合并统一成一个apk,这就是组件化开发.插件化开发和组件化开发略有不用,插件化开发时将整个app拆分成很多模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk(组件化的每个模块是个lib),最终

PHP 页面静态化/纯静态化/伪静态化

概念 PHP静态化分为:纯静态化 和 伪静态化:纯静态化又分为:局部静态化 和 完全静态化 纯静态化:是把PHP生成的动态页面保存成静态的html文件,用户访问该静态页面,而不是用户每一次访问都重新生成一张相同的网页,优点就是减小服务器开销, 局部静态化:是生成的静态文件中,有局部的数据还是通过ajax技术动态获取的: 完全静态化:即不存在动态获取数据的情况,所以内容都来自静态的html页面 伪静态化:其实还是动态访问,其实质是动态生成数据,你访问的网址类似于"http://yourhost,c

子类化和超类化区别(介绍Windows的窗口、消息、子类化和超类化)(转)

原文地址:http://maqianli210.blog.sohu.com/75497589.html 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和线程.子类化(Subclassing)和超类化(Superclassing)是伴随Windows窗口机制而产生的两个复用代码的方法.不要把“子类化.超类化”与面向对象语言中的派生类.基类混淆起来.“子类化.超类化”中的“类”是指Windows的窗口类. 0 运