5.4.3 工资程序成长记:函数

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

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

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

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

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

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

图5-11 工资程序模块图

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

  1. // 工资程序V2.0
  2. #include <iostream>
  3. // 为了读写文件,引入文件流对象头文件
  4. #include <fstream>
  5. #include <string>
  6. #include <climits> // 为了使用int类型的最大值和最小值
  7.  
  8. using namespace std;
  9.  
  10. // 全局的工资数据文件名,使用一个不可修改的常量字符串表示
  11. const string strFileName = "SalaryData.txt";
  12.  
  13. // 从数据文件读取工资数据到arrSalary数组
  14. int Read(int* arrSalary, int nCount)
  15. {
  16. int i = ; // 当前工资序号
  17.  
  18. // 打开工资数据文件SalaryData.txt用于读入数据
  19. // 这个文件应该在exe文件所在的相同目录下
  20. ifstream in(strFileName);
  21.  
  22. if(in.is_open()) // 如果成功打开数据文件
  23. {
  24. // 构造一个while循环读取文件中的工资数据
  25. // 如果读取的数据个数i小于数组的容量nCount,则继续读取
  26. while(i < nCount)
  27. {
  28. // 将读取的数据保存到arrSalary[i]
  29. in>>arrSalary[i];
  30. // 对读取结果进行判断,看是否读取到了文件结束
  31. // 如果到达文件结尾,则用break关键字结束读取循环
  32. if(!(in))
  33. {
  34. break;
  35. }
  36.  
  37. ++i; // 尚未读取完毕,开始下一次读取
  38. }
  39.  
  40. // 读取完毕,关闭文件
  41. in.close();
  42. }
  43. // 输出读取结果,返回读取的数据个数
  44. cout<<"读取"<<i<<"个工资数据"<<endl;
  45. return i;
  46. }
  47.  
  48. // 将arrSalary数组中的工资数据写入数据文件
  49. void Write(int* arrSalary, int nCount)
  50. {
  51. // 创建或打开工资数据文件SalaryData.txt用于输出
  52. // 输出完成后,这个文件将出现在exe文件所在的目录
  53. ofstream o(strFileName);
  54. if(o.is_open()) // 如果成功打开数据文件
  55. {
  56. // 利用for循环,将数组中的数据输出到文件
  57. for(int i = ;i < nCount;++i)
  58. {
  59. o<<arrSalary[i]<<endl; // 每一行一个数据
  60. }
  61.  
  62. // 输出完毕,关闭文件
  63. o.close();
  64. }
  65. }
  66.  
  67. // 获取工资数组中的最大值
  68. int GetMax(int* arrSalary, int nCount)
  69. {
  70. int nMax = INT_MIN; // 初始值为int类型的最小值
  71. // 利用for循环遍历数组中所有数据元素,逐个进行比较
  72. for(int i = ;i < nCount; ++i)
  73. {
  74. if(arrSalary[i] > nMax)
  75. nMax = arrSalary[i];
  76. }
  77. // 返回找到的最大值
  78. return nMax;
  79. }
  80.  
  81. // 获取数组中的最小值(请依最大值函数的葫芦自行画出最小值函数的瓢)…
  82.  
  83. // 计算数组中所有数据的平均值
  84. float GetAver(int* arrSalary, int nCount)
  85. {
  86. // 计算总和
  87. int nTotal = ;
  88. for(int i = ;i < nCount; ++i)
  89. {
  90. nTotal += arrSalary[i];
  91. }
  92.  
  93. // 计算平均值并返回
  94. if( != nCount) // 判断总数是否为0
  95. return (float)nTotal/nCount;
  96. else
  97. return 0.0f; // 特殊情况返回0
  98. }
  99.  
  100. // 手工输入数据
  101. int Input(int* arrSalary, // 工资数组首地址
  102. int nMax, // 数组能够容纳的数据的个数 //
  103. int nIndex) // 数组中已有的数据的个数
  104. {
  105. // 用数组中已有数据的个数作为输入的起点
  106. int i = nIndex; // 在for循环之前初始化i,应为i在for循环之后还需要用到
  107. for(; i < nMax; ++i) // i已经初始化,初始化语句留空
  108. {
  109. // 提示输入
  110. cout<<"请输入"<<i<<"号员工的工资(-1表示输入结束):"<<endl;
  111. // 将输入的数据保存到数组的arrSalary[i]数据元素
  112. int n = ;
  113. cin>>n;
  114. // 检查输入是否合法
  115. if(cin)
  116. {
  117. arrSalary[i] = n;
  118. }
  119. else// 如果输入不合法,例如输入了英文字符,则提示用户重新输入
  120. {
  121. cout<<"输入错误,请重新输入"<<endl;
  122. // 清理cin的输入标志位以重新输入
  123. cin.clear();
  124. // 清空输入缓冲区
  125. cin.sync();
  126. --i; // 将输入序号退后一个
  127. continue; // 直接开始下一次循环
  128. }
  129.  
  130. // 检查是否输入结束
  131. if(- == arrSalary[i])
  132. {
  133. break; // 结束输入循环
  134. }
  135. }
  136.  
  137. // 返回当前数组中共有的数据个数
  138. return i;
  139. }
  140.  
  141. // 查询工资数据
  142. void Find(int* arrSalary,int nCount)
  143. {
  144. while(true) // 构造无限循环进行工资查询
  145. {
  146. int n = ;
  147. // 提示用户输入要查询的员工序号
  148. cout<<"请输入要查询的员工序号(0-"<<nCount-
  149. <<",-1表示结束查询):"<<endl;
  150. // 获取用户输入的员工序号并保存到n
  151. cin>>n;
  152.  
  153. // 对用户输入进行检查
  154. if(!cin) // 如果用户输入不合法
  155. {
  156. cout<<"输入错误,请重新输入"<<endl;
  157. // 清理cin的输入标志位以重新输入
  158. cin.clear();
  159. // 清空输入缓冲区
  160. cin.sync();
  161. continue; // 开始下一次查询
  162. }
  163. else if(- == n) // 检查查询是否结束
  164. {
  165. // 查询结束,用break结束循环
  166. cout<<"查询完毕,感谢使用!"<<endl;
  167. break;
  168. }
  169. else if(n<||n>=nCount) // 检查输入是否超出序号范围
  170. {
  171. // 输入序号超出范围,用continue开始下一次循环
  172. cout<<"输入的序号"<<n<<"超出了序号范围0-"
  173. <<nCount-<<",请重新输入。"<<endl;
  174. // 开始下一次查询
  175. continue;
  176. }
  177.  
  178. // 输入合法,输出用户查询的员工工资
  179. cout<<"员工序号:"<<n<<endl;
  180. cout<<"员工工资:"<<arrSalary[n]<<endl;
  181. }
  182. }
  183.  
  184. int main()
  185. {
  186. // 定义保存员工数据的超大数组
  187. const int MAX = ;
  188. int arrSalary[MAX] = {};
  189.  
  190. // 首先从数据文件读取已经保存的数据
  191. int nCount = Read(arrSalary,MAX);
  192. // 然后用手工继续输入工资数据
  193. nCount = Input(arrSalary,MAX,nCount);
  194.  
  195. // 对输入的工资数据进行统计
  196. cout<<"输入完毕。一共有"<<nCount<<"个工资数据"<<endl;
  197. cout<<"最大值:"<<GetMax(arrSalary,nCount)<<endl;
  198. cout<<"最小值:"<<GetMin(arrSalary,nCount)<<endl;
  199. cout<<"平均值:"<<GetAver(arrSalary,nCount)<<endl;
  200.  
  201. // 对工资数据进行查询
  202. Find(arrSalary,nCount);
  203.  
  204. // 查询结束,将工资数据保存到数据文件,以备下次使用
  205. Write(arrSalary,nCount);
  206.  
  207. return ;
  208. }

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

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

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

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

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

    4.4  从语句到程序 了解了各种表达式和语句之后,就相当于掌握了写作文要用到的词语和句子,但是,仅有词语和句子是无法构成一篇有意义的文章的.要完成一篇文章,先需要确定这篇文章的结构,是先分述再总述, ...

  2. 你好,C++(38)从问题描述中发现对象的属性和行为 6.4 工资程序成长记:类与对象(上)

    6.4  工资程序成长记:类与对象 “夜半三更哟,盼天明:寒冬腊月哟,盼春风.若要盼得哟,涨工资,岭上……”自从上次老板许诺给小陈涨工资以后,一转眼又过去几个月了,可是涨工资的事一点动静都没有.小陈只 ...

  3. Android组件化和插件化开发

    http://www.cnblogs.com/android-blogs/p/5703355.html 什么是组件化和插件化? 组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Modul ...

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

    眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和 ...

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

    个人博客迁移至独立博客:https://blog.plcent.com/,欢迎大家访问 概念 PHP静态化分为:纯静态化 和 伪静态化:纯静态化又分为:局部静态化 和 完全静态化 纯静态化:是把PHP ...

  6. Android架构设计之插件化、组件化

    如今移动app市场已经是百花齐放,其中有不乏有很多大型公司.巨型公司都是通过app创业发展起来的:app类型更加丰富,有电子商务.有视频.有社交.有工具等等,基本上涵盖了各行各业每个角落,为了更加具有 ...

  7. [案例分析] 政务云市场面临的复杂格局——重庆政务云模式的启示:多厂商竞争化、PaaS 化

    新闻背景: 2019 年 9 月底,重庆市大数据应用发展管理局发布政务云平台采购公告,预算金额为 5000 万元,以上 4 家入选. 最终项目被项目被阿里云.腾讯云.华为云.紫光云 4 家瓜分. 50 ...

  8. C-RAN 集中化、协作化、云化、绿色节能(4C)

    中国移动C-RAN力拼第4个C:2018年6月外场组网验证 http://www.c114.net ( 2016/11/22 07:41 ) C114讯 11月22日早间消息(子月)2009年,中国移 ...

  9. 问你觉得iOS7为什么要扁平化,扁平化和之前的比有什么优势

    问你觉得iOS7为什么要扁平化,扁平化和之前的比有什么优势 苹果首席设计师谈为何会在iOS上选择扁平风格http://ndnews.oeeee.com/html/201306/11/71078.htm ...

随机推荐

  1. SPSS方差分析

    1.overall:一切的,全面地 单因素方差分析:分析--比较均值--单因素ANOVA.多因素方差分析:分析--一般线性模型--单变量. 单因素方差分析和单变量方差分析区别:单因素针对的是自变量(自 ...

  2. es watcher

    https://www.elastic.co/products/watcher https://www.elastic.co/blog/watcher-beta-goes-public-you-kno ...

  3. HDU Train Problem I 1022 栈模拟

    题目大意: 给你一个n 代表有n列 火车,  第一个给你的一个字符串 代表即将进入到轨道上火车的编号顺序, 第二个字符串代表的是 火车出来之后到顺序, 分析一下就知道这,这个问题就是栈, 先进后出吗, ...

  4. 使用C#开发ActiveX控件 11

    C#开发ActiveX控件   ActiveX 是一个开放的集成平台,为开发人员.用户和 Web生产商提供了一个快速而简便的在 Internet 和 Intranet 创建程序集成和内容的方法. 使用 ...

  5. VirtualBox - 自动调整屏幕大小,显示分辨率

    在VirtualBox中安装了Ubuntu后,Ubuntu的屏幕调整不太好,操作起来非常不方便,需要安装Vbox的增强功能.具体如下:1, 在  设备--> 安装增强功能这时会自动加载VBOXA ...

  6. delphi 通过控件的handle取得控件

    例子代码如下: vartsg:TstringGrid;begintsg:=Tstringgrid(FindControl(handle));//正常使用TstringGrid//tsg......./ ...

  7. 动态规划初级练习(二):BadNeighbors

    Problem Statement      The old song declares "Go ahead and hate your neighbor", and the re ...

  8. Linux下DVD-R刻录问题

    之前CD的刻录一直使用的命令行工具集cdrtools中的mkisofs.cdrecord.然后本来刻录DVD可以使用它的growisofs命令. 现在假设原始文件目录为/src/,目标目录为/dest ...

  9. centos 6.2 关闭 IPV6

    在现在的Linux上IPv6已经在默认安装下被支持,但是对于一些对IPv6支持不是很好的应用服务器来说,开启了IPv6反而会影响服务器的网络性能,毕竟现在的网络交换设备不是IPv6的. 如何判断系统是 ...

  10. JMeter在里面Json数据处理方法

    http://eclipsesource.com/blogs/2014/06/12/parsing-json-responses-with-jmeter/ Json作为一种数据交换格式在网络开发.特别 ...