本书的第三章中,我们看到很多处理批量数据的方法,通过编写一个程序,读取学生的考试和家庭作业成绩,并且计算出一个最终的成绩。并且在这个过程中,我们还学习了如何储存所有数据,即使我们开始并不知道有多少成绩需要储存。
第四章中,提供了两个来组织大型程序的基本办法:函数(有时候我们叫子程序)和数据结构。
通过第三章和第四章的学习,我们将书本中的程序进行汇总,可得到一个完整的学生成绩统计与计算的小程序,下面我简单讲所学所编进行记录:
#include <stdexcept> #include <vector> #include "grade.h" #include "median.h" #include "Student_info.h" using namespace std; //重载就是函数名相同,但是由于形参不同,所以机器分别的出来 //用方法1计算最终个人成绩 double grade(double midterm, double final, double homework) { return 0.2*midterm + 0.4*final + 0.4*homework; } //如果没有家庭作业成绩就报警,再用方法2(家庭成绩取中值)计算个人最终成绩 //该函数的形参需要注意四点 //1.const vector<double>& hw 该形参的类型是vector<double>,所以对应的实参也必须是这个类型的 //2.double final 当调用grade函数时,对应double final的实参对象就会被复制到 final 中, // 而不会改变原来的实参double对象 //3.const vector<double>& hw 注意 & 的这个东西,& 是用做别名(引用)的意思,这时候就不同于第二点(复制), // 这里不复制,直接用实参对象,并且能够改变实参内容 //4.const vector<double>& hw 注意 const ,这又不同于第三点,这个时候函数的形参也是直接用实参,但是不能改变其内容 double grade(double midterm, double final, const vector<double>& hw) { if (hw.size() == 0) throw domain_error("学生没有家庭作业成绩"); return grade(midterm, final, median(hw)); } //利用Student_info这个结构,返回函数grade(方法2)计算的值 double grade(const Student_info& s) { return grade(s.midterm, s.final, s.homework); }
#include "median.h" #include <vector> #include <algorithm> #include <stdexcept>//包含异常类 domain_error using namespace std; //中间排序函数,用来找到中间值 //当调用median函数时,用作实参的vector对象就会被复制到vec中,从而不会改变原来的实参vector对象 double median(vector<double> vec) { typedef vector<double>::size_type vec_sz; vec_sz size = vec.size(); if (size == 0) throw domain_error("是一个空数据"); //如果size为0,则会抛出一个异常throw,终止当前函数,并进行错误提示。这次抛出的异常是 domain_error。 sort(vec.begin(), vec.end());//排序函数,比较的元素类型必须要相同 vec_sz mid = size / 2; return size % 2 == 0 ? (vec[mid] + vec[mid + 1]) / 2 : vec[mid];//判断时偶数还是奇数,并最后返回一个中间值 }
#include "Student_info.h" //引用Student_info.h头文件 using namespace std; //比较Student_info结构中name的大小,返回一个bool值,为sort()函数排序当参数用的 bool compare(const Student_info& x, const Student_info& y) { return x.name < y.name; } //用于程序输入姓名,期中,期末考试和家庭成绩 istream& read(istream& is, Student_info& s) { is >> s.name >> s.midterm >> s.final; cout << "读取家庭成绩" << endl; //注意在读取家庭作业时,由于read_hw内使用的while 来收集某个同学家庭成绩的,所以需要用ctrl+z来跳出某个同学的家庭作业循环 read_hw(is,s.homework); return is; } //用于程序输入家庭成绩 //这里多讲几句 //1.首先read_hw函数中有两个形参,均因为&引用,所以都直接用的是实参的内容(不复制,可改内容) //2.其次,形参istream& in 为什么要用引用,因为从标准输入文件中读取会改变文件的状态,所以也要改变cin的值 //3.最后read_hw函数返回的是形参 in ,而且形参还是一个引用,也就是说,调用程序给read_hw函数一个对象,最后返回同一个对象。 istream& read_hw(istream& in, vector<double>& hw) { if (in){ hw.clear();//因为不清楚定义vector类的hw时里面有没有其他值,所以先清除一下hw double x; while (in>>x) hw.push_back(x);//push_back成员用于将x值压入hw(vector)中,且hw(vector)的数量+1 in.clear();//因为可能我们输入的不是成绩,可能错误的敲了一个字母,这时候会结束我们的程序, //而这时候的in(istream类)的clear就会把错误清除,恢复正常读取过程, //而且不用这个清除函数的话,会影响后面的结构体的输入 } return in; }
#include <algorithm> #include <iomanip> #include <ios> #include <iostream> #include <stdexcept> #include <string> #include <vector> #include "grade.h" #include "Student_info.h" using namespace std; int main() { vector<Student_info> students; Student_info record; string::size_type maxlen = 0; int k = 1; cout << "请输入第" << k << "个同学的信息" << endl; while (read(cin,record)) { maxlen = max(maxlen, record.name.size()); k++; students.push_back(record); cout << "请输入第" << k << "个同学的信息" << endl; } sort(students.begin(), students.end(),compare); //不要忘记begin后面的括号 for (vector<Student_info>::size_type i = 0; i != students.size(); ++i) { cout << students[i].name << string(maxlen + 1 - students[i].name.size(), ' '); try { double final_grade = grade(students[i]); //double final_grade = grade(students[i].midterm, students[i].final, students[i].homework); streamsize prec = cout.precision(); cout << setprecision(3) << final_grade << setprecision(prec); } catch (domain_error e) { cout << e.what(); } cout << endl; } system("pause"); //如果没有这个,控制台就直接跳出,不会停留 return 0; }
已经打包上传,可见:https://download.csdn.net/download/baidu_38346905/19114932