实验4 类的组合、继承、模板类、标准库

实验任务2

GradeCalc.hpp源码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <iomanip>

using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::endl;

class GradeCalc: public vector<int> {
public:
    GradeCalc(const string &cname, int size);      
    void input();                             // 录入成绩
    void output() const;                      // 输出成绩
    void sort(bool ascending = false);        // 排序 (默认降序)
    int min() const;                          // 返回最低分
    int max() const;                          // 返回最高分
    float average() const;                    // 返回平均分
    void info();                              // 输出课程成绩信息 

private:
    void compute();     // 成绩统计

private:
    string course_name;     // 课程名
    int n;                  // 课程人数
    vector<int> counts = vector<int>(5, 0);      // 保存各分数段人数([0, 60), [60, 70), [70, 80), [80, 90), [90, 100]
    vector<double> rates = vector<double>(5, 0); // 保存各分数段比例 
};

GradeCalc::GradeCalc(const string &cname, int size): course_name{cname}, n{size} {}   

void GradeCalc::input() {
    int grade;

    for(int i = 0; i < n; ++i) {
        cin >> grade;
        this->push_back(grade);
    } 
}  

void GradeCalc::output() const {
    for(auto ptr = this->begin(); ptr != this->end(); ++ptr)
        cout << *ptr << " ";
    cout << endl;
} 

void GradeCalc::sort(bool ascending) {
    if(ascending)
        std::sort(this->begin(), this->end());
    else
        std::sort(this->begin(), this->end(), std::greater<int>());
}  

int GradeCalc::min() const {
    return *std::min_element(this->begin(), this->end());
}  

int GradeCalc::max() const {
    return *std::max_element(this->begin(), this->end());
}    

float GradeCalc::average() const {
    return std::accumulate(this->begin(), this->end(), 0) * 1.0 / n;
}   

void GradeCalc::compute() {
    for(int grade: *this) {
        if(grade < 60)
            counts.at(0)++;
        else if(grade >= 60 && grade < 70)
            counts.at(1)++;
        else if(grade >= 70 && grade < 80)
            counts.at(2)++;
        else if(grade >= 80 && grade < 90)
            counts.at(3)++;
        else if(grade >= 90)
            counts.at(4)++;
    }

    for(int i = 0; i < rates.size(); ++i)
        rates.at(i) = counts.at(i) * 1.0 / n;
}

void GradeCalc::info()  {
    cout << "课程名称:\t" << course_name << endl;
    cout << "排序后成绩: \t";
    sort();  output();
    cout << "最高分:\t" << max() << endl;
    cout << "最低分:\t" << min() << endl;
    cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << endl;
    
    compute();  // 统计各分数段人数、比例

    vector<string> tmp{"[0, 60)  ", "[60, 70)", "[70, 80)","[80, 90)", "[90, 100]"};
    for(int i = tmp.size()-1; i >= 0; --i)
        cout << tmp[i] << "\t: " << counts[i] << "人\t" 
             << std::fixed << std::setprecision(2) << rates[i]*100 << "%" << endl; 
} 

task2.cpp源码

#include "GradeCalc.hpp"
#include <iomanip>

void test() {
    int n;
    cout << "输入班级人数: ";
    cin >> n;

    GradeCalc c1("OOP", n);

    cout << "录入成绩: " << endl;;
    c1.input();
    cout << "输出成绩: " << endl;
    c1.output();

    cout << string(20, '*') + "课程成绩信息"  + string(20, '*') << endl;
    c1.info();
}

int main() {
    test();
}

运行测试截图

问题1:

派生类GradeCalc定义中,成绩存储在哪里?派生类方法sort, min, max, average,
output都要访问成绩,是通过什么接口访问到每个成绩的?input方法是通过什么接口实现数据存入对象的?

成绩存储在GradeCalc对象内部,实际上是存储在GradeCalc继承的vector基类的实例中。当创建一个GradeCalc对象时,也创建了一个能够存储整数的vector。

由于GradeCalc继承自vector,它可以直接使用vector的接口来访问和修改成绩。例如,sort、min、max、average和output方法都通过this->begin()和this->end()来访问vector的开始和结束迭代器,从而能够遍历和操作存储在其中的成绩。

input方法通过调用this->push_back(grade)来将用户输入的成绩添加到GradeCalc对象(实际上是内部的vector)的末尾。这里,this指针指向当前的GradeCalc对象,而push_back是vector的一个成员函数,用于在容器的末尾添加一个元素。

问题2:

代码line68分母的功能是?去掉乘以1.0代码,重新编译、运行,结果有影响吗?为什么要乘以1.0?

GradeCalc::average() 方法的功能是计算并返回成绩的平均值。在这个方法中,使用了 std::accumulate 函数来计算所有成绩的总和,然后将总和除以成绩的数量 n 来得到平均值。
在表达式 std::accumulate(this->begin(), this->end(), 0) * 1.0 / n 中,乘以 1.0 是为了确保结果是浮点数类型(float 或 double),而不是整数。这是因为在 C++ 中,如果两个整数进行除法运算,结果也会是整数,小数部分会被截断。
乘以 1.0 将总和转换为浮点数,然后再除以 n,这样得到的结果就是一个浮点数,能够表示平均值的精确值(包括小数部分)。

问题3:

从真实应用场景角度考虑,GradeCalc类在设计及代码实现细节上,有哪些地方尚未考虑周全,仍需继续迭代、完善?

1. 数据结构和性能

  • 成绩存储优化:考虑使用其他合适的数据结构,例如 std::mapstd::unordered_map 来存储成绩及其统计信息。这可以提高查找和处理的效率,特别是在处理大量数据时。
  • 动态调整容量:优化成绩存储的方式,动态调整所分配的数据结构的容量,以减少冗余内存使用。

2. 功能增强

  • 成绩排名:增加学生成绩的排名功能,包括名次、百分位等统计信息,提供更全面的成绩分析。
  • 不同科目支持:设计为可以处理多个科目及其成绩,允许每个科目有独立的数据结构和统计,使得系统更加通用。
  • 课程与学生信息管理:可以将每门课程的学生信息和成绩结合在一起,形成一个完整的管理系统,并提供对学生的增改查功能。

实验任务3

GradeCalc.hpp源码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <iomanip>

using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::endl;

class GradeCalc {
public:
    GradeCalc(const string &cname, int size);      
    void input();                             // 录入成绩
    void output() const;                      // 输出成绩
    void sort(bool ascending = false);        // 排序 (默认降序)
    int min() const;                          // 返回最低分
    int max() const;                          // 返回最高分
    float average() const;                    // 返回平均分
    void info();                              // 输出课程成绩信息 

private:
    void compute();     // 成绩统计

private:
    string course_name;     // 课程名
    int n;                  // 课程人数
    vector<int> grades;     // 课程成绩
    vector<int> counts = vector<int>(5, 0);      // 保存各分数段人数([0, 60), [60, 70), [70, 80), [80, 90), [90, 100]
    vector<double> rates = vector<double>(5, 0); // 保存各分数段比例 
};

GradeCalc::GradeCalc(const string &cname, int size): course_name{cname}, n{size} {}   

void GradeCalc::input() {
    int grade;

    for(int i = 0; i < n; ++i) {
        cin >> grade;
        grades.push_back(grade);
    } 
}  

void GradeCalc::output() const {
    for(int grade: grades)
        cout << grade << " ";
    cout << endl;
} 

void GradeCalc::sort(bool ascending) {
    if(ascending)
        std::sort(grades.begin(), grades.end());
    else
        std::sort(grades.begin(), grades.end(), std::greater<int>());
        
}  

int GradeCalc::min() const {
    return *std::min_element(grades.begin(), grades.end());
}  

int GradeCalc::max() const {
    return *std::max_element(grades.begin(), grades.end());
}    

float GradeCalc::average() const {
    return std::accumulate(grades.begin(), grades.end(), 0) * 1.0 / n;
}   

void GradeCalc::compute() {
    for(int grade: grades) {
        if(grade < 60)
            counts.at(0)++;
        else if(grade >= 60 && grade < 70)
            counts.at(1)++;
        else if(grade >= 70 && grade < 80)
            counts.at(2)++;
        else if(grade >= 80 && grade < 90)
            counts.at(3)++;
        else if(grade >= 90)
            counts.at(4)++;
    }

    for(int i = 0; i < rates.size(); ++i)
        rates.at(i) = counts.at(i) *1.0 / n;
}

void GradeCalc::info()  {
    cout << "课程名称:\t" << course_name << endl;
    cout << "排序后成绩: \t";
    sort();  output();
    cout << "最高分:\t" << max() << endl;
    cout << "最低分:\t" << min() << endl;
    cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << endl;
    
    compute();  // 统计各分数段人数、比例

    vector<string> tmp{"[0, 60)  ", "[60, 70)", "[70, 80)","[80, 90)", "[90, 100]"};
    for(int i = tmp.size()-1; i >= 0; --i)
        cout << tmp[i] << "\t: " << counts[i] << "人\t" 
             << std::fixed << std::setprecision(2) << rates[i]*100 << "%" << endl; 
}

task3.cpp源码

#include "GradeCalc.hpp"
#include <iomanip>

void test() {
    int n;
    cout << "输入班级人数: ";
    cin >> n;

    GradeCalc c1("OOP", n);

    cout << "录入成绩: " << endl;;
    c1.input();
    cout << "输出成绩: " << endl;
    c1.output();

    cout << string(20, '*') + "课程成绩信息"  + string(20, '*') << endl;
    c1.info();
}

int main() {
    test();
}

运行测试截图

问题1:

组合类GradeCalc定义中,成绩存储在哪里?组合类方法sort, min, max, average,
output都要访问成绩,是通过什么访问到每一个成绩的?观察与实验任务2在代码写法细节上的差别。

在 GradeCalc 类的定义中,成绩是存储在 grades 这个成员变量中的。这个成员变量是一个 vector 类型的对象,用于动态存储录入的学生成绩。

访问成绩的方法:
组合类方法 sort, min, max, average, 和 output 都通过 grades 成员变量来访问成绩。具体来说:
sort使用 std::sort(grades.begin(), grades.end()) 对 grades 中的成绩进行排序。
min 使用 std::min_element(grades.begin(), grades.end()) 来计算成绩中的最低值。
max使用 std::max_element(grades.begin(), grades.end()) 来找出成绩中的最高值。
average 使用 std::accumulate(grades.begin(), grades.end(), 0) 来计算所有成绩的总和,然后除以人数 n 得到平均分。
output 通过遍历 grades 来输出每个成绩。

细节差别:
成绩存储:在之前的代码中,成绩是通过继承 vector 类来存储的,所有与成绩相关的方法都直接操作该 vector,而在当前代码中,成绩是作为 grades 成员变量单独定义的,这样使得类的结构更加清晰且便于维护。
方法实现:之前的实现将一些成绩的统计和计算逻辑放置在类成员函数之外,通过对继承的 vector 直接操作;当前版本则将所有相关功能封装在 GradeCalc 类内部,增加了类的封装性,使得上下文更明确。

问题2:

对比实验任务2和实验任务3,主体代码逻辑(测试代码)没有变更,类GradeCalc的接口也没变,变化的是类GradeCalc的设计及接口内部实现细节。你对面向对象编程有什么新的理解和领悟吗?

  1. 封装性
    在 OOP 中,封装性是指将数据及操作数据的方法封闭在类的内部,只对外暴露必要的接口。实验任务3的实现通过将成绩存储在 grades 成员变量中,并通过方法访问和操作这些数据,增强了数据的封装性。这种做法有助于保护数据的完整性,防止外部代码直接修改 grades,从而降低了潜在的错误发生。例如, input() 方法负责读取用户输入并填充成绩数组,而不是允许外部直接操作 grades。

  2. 抽象与接口
    尽管接口没有变化,但内部实现的细节变化展示了如何利用抽象提升代码的灵活性和可维护性。在 OOP 中,良好的设计意味着实现细节可以随时更改,而不会影响使用类的其他部分。实验任务3通过引入 compute() 方法来集中处理分数段的统计逻辑,这意味着如果以后需要更改统计方式,只需修改该方法,而不必修改调用此方法的所有地方,从而提升了可维护性。

  3. 代码可重用性与组合
    实验任务3的设计也体现了代码的可重用性与组合的思想。在这个设计中,GradeCalc 类不仅负责成绩的输入和输出,还负责成绩的评估和统计,通过分层处理使得每个方法职责单一,更加清晰。这样设计出的类可以在不同的应用场景中重复使用,只需调用相应的接口。

  4. 维护性与可扩展性
    面向对象编程让代码更容易维护和扩展。由于实验任务3的 GradeCalc 类结构清晰,各个功能模块间的耦合度较低,因此在将来需要扩展功能(比如新增科目成绩、修改统计方式等)时,只需对相关部分进行修改,减少了对整体代码的影响,使得系统更具可扩展性。

实验任务4

task4_1.cpp源码

#include <iostream>
#include <string>
#include <limits>

using namespace std;

void test1() {
    string s1, s2;
    cin >> s1 >> s2;  // cin: 从输入流读取字符串, 碰到空白符(空格/回车/Tab)即结束
    cout << "s1: " << s1 << endl;
    cout << "s2: " << s2 << endl;
}

void test2() {
    string s1, s2;
    getline(cin, s1);  // getline(): 从输入流中提取字符串,直到遇到换行符
    getline(cin, s2);
    cout << "s1: " << s1 << endl;
    cout << "s2: " << s2 << endl;
}

void test3() {
    string s1, s2;
    getline(cin, s1, ' '); //从输入流中提取字符串,直到遇到指定分隔符
    getline(cin, s2);
    cout << "s1: " << s1 << endl;
    cout << "s2: " << s2 << endl;
}

int main() {
    cout << "测试1: 使用标准输入流对象cin输入字符串" << endl;
    test1();
    cout << endl;

    cin.ignore(numeric_limits<streamsize>::max(), '\n');

    cout << "测试2: 使用函数getline()输入字符串" << endl;
    test2();
    cout << endl;

    cout << "测试3: 使用函数getline()输入字符串, 指定字符串分隔符" << endl;
    test3();
}

运行测试截图

问题1:

去掉task4_1.cpp的line35,重新编译、运行,给出此时运行结果截图。查阅资料,回答line35在这里的用途是什么?

去掉line35之后运行结果截图:

cin.ignore(numeric_limits::max(), '\n'); 这一行的用途主要是用来清除输入缓冲区中的多余字符,特别是在处理不同输入方式之间的过渡时,以避免因为残留的换行符导致输入读取失败或行为异常。

当程序读取完 test1() 的输入后,如果输入包括换行符,cin 仍然会在输入缓冲区中留下这个换行符。接下来,如果程序想要读取另一行字符串(在 test2() 和 test3() 中),这个残留的换行符会立刻被getline()读取,从而导致它返回一个空字符串。

cin.ignore 的作用:
cin.ignore() 函数的作用是跳过输入流中的某些字符。在这里,它被用来忽略输入缓冲区中可能残留的字符。
numeric_limits::max() 指定忽略的字符数量为输入流中可能的最大数量,这意味着它会尽可能忽略所有字符,直到找到指定的标记或到达结束标志。
'\n' 参数则指定了要忽略的字符,直到遇到换行符为止。在这个上下文中,它会清除掉输入中的换行符,确保下一次 getline() 调用时能够按预期读取新行的输入。

task4_2.cpp源码

#include <iostream>
#include <string>
#include <vector>
#include <limits>

using namespace std;

void output(const vector<string> &v) {
    for(auto &s: v)
        cout << s << endl;
}

void test() {
    int n;
    while(cout << "Enter n: ", cin >> n) {
        vector<string> v1;

        for(int i = 0; i < n; ++i) {
            string s;
            cin >> s;
            v1.push_back(s);
        }

        cout << "output v1: " << endl;
        output(v1); 
        cout << endl;
    }
}

int main() {
    cout << "测试: 使用cin多组输入字符串" << endl;
    test();
}

运行测试截图

task4_3.cpp源码

#include <iostream>
#include <string>
#include <vector>
#include <limits>

using namespace std;

void output(const vector<string> &v) {
    for(auto &s: v)
        cout << s << endl;
}

void test() {
    int n;
    while(cout << "Enter n: ", cin >> n) {
        cin.ignore(numeric_limits<streamsize>::max(), '\n');

        vector<string> v2;

        for(int i = 0; i < n; ++i) {
            string s;
            getline(cin, s);
            v2.push_back(s);
        }
        cout << "output v2: " << endl;
        output(v2); 
        cout << endl;
    }
}

int main() {
    cout << "测试: 使用函数getline()多组输入字符串" << endl;
    test();
}

运行测试截图

问题2:

去掉task4_3.cpp的line16,重新编译、运行,给出此时运行结果。查阅资料,回答line167在这里的用途是什么?

运行结果

在这段代码中,cin.ignore(numeric_limits::max(), '\n'); 的作用与之前提到的用法类似,主要用于处理输入缓冲区的换行符,以确保在使用 getline() 函数读取字符串时能够顺利读取到用户输入。
while(cout << "Enter n: ", cin >> n) 这一行提示用户输入一个整数 n,并将其存储在变量 n 中。
这里 cin 会读取用户输入,并在读取整数之后遇到换行符(即用户按下 Enter 键)。换行符会留在输入缓冲区中。
使用 cin.ignore():
当用户输入了一个整数后,缓冲区中会留下一个换行符,这个换行符会被后续的 getline() 函数读取,引发问题,即 getline() 秒读到这个换行符,直接返回一个空字符串。
为了避免这个问题,我们在读取整数之后使用 cin.ignore(numeric_limits::max(), '\n');,这将会忽略输入缓冲区中所有内容,直到遇到换行符。这意味着任何剩余的输入(如换行)都会被跳过。

实验任务5

grm.hpp源码

#include<iostream>

using namespace std;

template<typename T>
class GameResourceManager{
public:
	GameResourceManager(T x=0);
	T get();
	void update(T x);
	
private:
	T resource;
};

template<typename T>
GameResourceManager<T>::GameResourceManager(T x){
	resource=x;
}

template<typename T>
T GameResourceManager<T>::get(){
	return resource;
}

template<typename T>
void GameResourceManager<T>::update(T x){
	resource+=x;
}

task5.cpp源码

#include "grm.hpp"
#include <iostream>

using std::cout;
using std::endl;

void test1() {
    GameResourceManager<float> HP_manager(99.99);
    cout << "当前生命值: " << HP_manager.get() << endl;
    HP_manager.update(9.99);
    cout << "增加9.99生命值后, 当前生命值: " << HP_manager.get() << endl;
    HP_manager.update(-999.99);
    cout <<"减少999.99生命值后, 当前生命值: " << HP_manager.get() << endl;
}

void test2() {
    GameResourceManager<int> Gold_manager(100);
    cout << "当前金币数量: " << Gold_manager.get() << endl;
    Gold_manager.update(50);
    cout << "增加50个金币后, 当前金币数量: " << Gold_manager.get() << endl;
    Gold_manager.update(-99);
    cout <<"减少99个金币后, 当前金币数量: " << Gold_manager.get() << endl;
}


int main() {
    cout << "测试1: 用float类型对类模板GameResourceManager实例化" << endl;
    test1();
    cout << endl;

    cout << "测试2: 用int类型对类模板GameResourceManager实例化" << endl;
    test2();
}

运行测试截图

实验任务6

info.hpp源码

#include<iostream>
#include<string>
#include<vector>
#include<iomanip>

using namespace std;

class info{
public:
	info(string na,string co,string ci,int n);
	void display() const;
	
private:
	string name,contact,city;
	int n;
};

info::info(string na,string co,string ci,int n):name{na},contact{co},city{ci}{
	this->n=n;
}

void info::display() const{
	cout<<left<<setw(16)<<"昵称:"<<name<<endl;
	cout<<left<<setw(16)<<"联系方式:"<<contact<<endl;
	cout<<left<<setw(16)<<"所在城市:"<<city<<endl;
	cout<<left<<setw(16)<<"预定人数"<<n<<endl;
	
	for(int i=0;i<30;i++){
		cout<<"-";
	}
	cout<<endl;
}

task6.cpp源码

#include<iostream>
#include<string>
#include<vector>
#include<iomanip>
#include"info.hpp"

int main(){
	const int capacity=100;
	vector<info> audience_list;
	int sum=0;
	string name,contact,city;
	int num;
	
	cout<<"录入用户信息:"<<endl<<endl;
	cout<<left<<setw(16)<<"昵称"<<setw(16)<<"联系方式(邮箱/手机号)"<<setw(16)<<"所在城市"<<setw(16)<<"预定参加人数"<<endl;
	
	while(cin>>name){
		if(name=="finish"){
			break;
		}
		cin>>contact>>city>>num;
		if(num>(capacity-sum)){
			cout<<"对不起,只剩"<<capacity-sum<<"个位置。" <<endl;
			cout<<"1.输入u,更新(update)预定信息"<<endl;
			cout<<"2.输入q,推出预定"<<endl;
			cout<<"你的选择:";
			char c;
			cin>>c;
			if(c=='u'){
				cout<<"请重新输入预定信息:"<<endl;
				continue;
			}
			else if(c=='q'){
				return 0;
			}
		}
		
		info x(name,contact,city,num);
		audience_list.push_back(x);
		sum+=num;
		if(sum==capacity){
			break;
		} 
	}
	
	cout<<"截至目前,一共有"<<sum<<"位听众预约。预约听众信息如下:"<<endl;
	for(int i=0;i<30;i++){
		cout<<"-";
	}
	cout<<endl;
	for(auto i:audience_list){
		i.display();
	}
}

运行测试截图


实验任务7

date.h源码

#ifndef __DATE_H__
#define __DATE_H__
class Date{
    private:
        int year;
        int month;
        int day;
        int totalDays;
    public:
        Date(int year,int month,int day);
        int getYear() const {return year;}
        int getMonth() const {return month;}
        int getDay() const {return day;}
        int getMaxDay() const;
        bool isLeapYear() const {
            return year%4==0 && year%100 !=0 ||year%400==0;
        }
        void show() const;

        int distance(const Date& date) const {
            return totalDays-date.totalDays;
        }
};
#endif //__DATE_H__

date.cpp源码

#include "date.h"
#include <iostream>
#include <cstdlib>
using namespace std;
namespace {
    const int DAYS_BEFORE_MONTH[]={0,31,59,90,120,151,181,212,243,273,304,334,365};
}
Date::Date(int year,int month,int day):year(year),month(month),day(day) {
    if(day<=0||day>getMaxDay()) {
        cout<<"Invalid date:";
        show();
        cout<<endl;
        exit(1);
    }
    int years=year-1;
    totalDays = years *365+years/4-years/100+years/400+DAYS_BEFORE_MONTH[month-1]+day;
    if(isLeapYear()&&month>2) totalDays++;
}

int Date::getMaxDay() const {
    if(isLeapYear()&&month==2)
        return 29;
    else
        return DAYS_BEFORE_MONTH[month]-DAYS_BEFORE_MONTH[month-1];
}

void Date::show()  const {
    cout<<getYear()<<"-"<<getMonth()<<"-"<<getDay();
}

accumulator.h源码

#pragma once
#ifndef  ACCUMULATOR H
#define  ACCUMULATOR H
#include"date.h"
class Accumulator {
private:
    Date lastDate;
    double value;
    double sum;
public:
    Accumulator(const Date& date, double value) :lastDate(date), value(value), sum{ 0 } {
    }

    double getSum(const Date& date)const {
        return sum + value * date.distance(lastDate);
    }

    void change(const Date& date, double value) {
        sum = getSum(date);
        lastDate = date; this->value = value;
    }

    void reset(const Date& date, double value) {
        lastDate = date; this->value = value; sum = 0;
    }
};
#endif//ACCUMULATOR H

account.h源码

#pragma once
#ifndef  ACCOUNT H
#define  ACCOUNT H
#include"date.h"
#include"accumulator.h"
#include<string>
class Account {
private:
    std::string id;
    double balance;
    static double total;
protected:
    Account(const Date& date, const std::string& id);
    void record(const Date& date, double amount, const std::string& desc);
    void error(const std::string& msg)const;
public:
    const std::string& getId()const { return id; }
    double getBalance()const { return balance; }
    static double getTotal() { return total; }

    void show()const;
};
class SavingsAccount :public Account {
private:
    Accumulator acc;
    double rate;
public:
    SavingsAccount(const Date& date, const std::string& id, double rate);
    double getRate()const { return rate; }

    void deposit(const Date& date, double amount, const std::string& desc);
    void withdraw(const Date& date, double amount, const std::string& desc);
    void settle(const Date& date);
};
class CreditAccount :public Account {
private:
    Accumulator acc;
    double credit;
    double rate;
    double fee;
    double getDebt()const {
        double balance = getBalance();
        return (balance < 0 ? balance : 0);
    }
public:
    CreditAccount(const Date& date, const std::string& id, double credit, double rate, double fee);
    double getCredit()const { return credit; }
    double getRate()const { return rate; }
    double getAvailableCredit()const {
        if (getBalance() < 0)
            return credit + getBalance();
        else
            return credit;
    }
    void deposit(const Date& date, double amount, const std::string& desc);
    void withdraw(const Date& date, double amount, const std::string& desc);
    void settle(const Date& date);
    void show()const;
};
#endif//ACCOUNT H

account.cpp源码

#include"account.h"
#include<cmath>
#include<iostream>
using namespace std;
double Account::total = 0;

Account::Account(const Date& date, const string& id) :id{ id }, balance{ 0 } {
    date.show(); cout << "\t#" << id << "created" << endl;
}


void Account::record(const Date& date, double amount, const string& desc) {
    amount = floor(amount * 100 + 0.5) / 100;
    balance += amount;
    total += amount;
    date.show();
    cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}

void Account::show()const { cout << id << "\tBalance:" << balance; }
void Account::error(const string& msg)const {
    cout << "Error(#" << id << "):" << msg << endl;
}

SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) :Account(date, id), rate(rate), acc(date, 0) {}

void SavingsAccount::deposit(const Date& date, double amount, const string& desc) {
    record(date, amount, desc);
    acc.change(date, getBalance());
}

void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) {
    if (amount > getBalance()) {
        error("not enough money");
    }
    else {
        record(date, -amount, desc);
        acc.change(date, getBalance());
    }
}

void SavingsAccount::settle(const Date& date) {
    double interest = acc.getSum(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1));
    if (interest != 0)record(date, interest, "interest");
    acc.reset(date, getBalance());
}

CreditAccount::CreditAccount(const Date& date, const string& id, double credit, double rate, double fee) :Account(date, id), credit(credit), rate(rate), fee(fee), acc(date, 0) {}

void CreditAccount::deposit(const Date& date, double amount, const string& desc) {
    record(date, amount, desc);
    acc.change(date, getDebt());
}

void CreditAccount::withdraw(const Date& date, double amount, const string& desc) {
    if (amount - getBalance() > credit) {
        error("not enough credit");
    }
    else {
        record(date, -amount, desc);
        acc.change(date, getDebt());
    }
}

void CreditAccount::settle(const Date& date) {
    double interest = acc.getSum(date) * rate;
    if (interest != 0)record(date, interest, "interest");
    if (date.getMonth() == 1)
        record(date, -fee, "annual fee");
    acc.reset(date, getDebt());
}

void CreditAccount::show()const {
    Account::show();
    cout << "\tAvailable credit:" << getAvailableCredit();
}

7_10.cpp源码

#include"account.h"
#include<iostream>

using namespace std;

int main() {
    Date date(2008, 11, 1);
    SavingsAccount sa1(date, "S3755217", 0.015);
    SavingsAccount sa2(date, "02342342", 0.015);
    CreditAccount ca(date, "C5392394", 10000, 0.0005, 50);

    sa1.deposit(Date(2008, 11, 5), 5000, "salary");
    ca.withdraw(Date(2008, 11, 15), 2000, "buy a cell");
    sa2.deposit(Date(2008, 11, 25), 10000, "sell stock 0323");

    ca.settle(Date(2008, 12, 1));

    ca.deposit(Date(2008, 12, 1), 2016, "repay the credit");
    sa1.deposit(Date(2008, 12, 5), 5500, "salary");

    sa1.settle(Date(2009, 1, 1));
    sa2.settle(Date(2009, 1, 1));
    ca.settle(Date(2009, 1, 1));

    cout << endl;
    sa1.show(); cout << endl;
    sa2.show(); cout << endl;
    ca.show(); cout << endl;
    cout << "Total:" << Account::getTotal() << endl;
    return 0;
}

运行测试截图

posted @ 2024-11-24 23:34  奇衡幽弥  阅读(2)  评论(0编辑  收藏  举报