《Accelerated C++中文版》学习配套编程(一)
本书的第三章中,我们看到很多处理批量数据的方法,通过编写一个程序,读取学生的考试和家庭作业成绩,并且计算出一个最终的成绩。并且在这个过程中,我们还学习了如何储存所有数据,即使我们开始并不知道有多少成绩需要储存。
第四章中,提供了两个来组织大型程序的基本办法:函数(有时候我们叫子程序)和数据结构。
通过第三章和第四章的学习,我们将书本中的程序进行汇总,可得到一个完整的学生成绩统计与计算的小程序,下面我简单讲所学所编进行记录:
1.grade.cpp
#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);
}
2.median.cpp
#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];//判断时偶数还是奇数,并最后返回一个中间值
}
3.Student_info.cpp
#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;
}
4.主程序
#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