清华大学C++程序设计(郑莉)综合实践
前言
课程综合实践中实现了模拟银行,用户可以添加储蓄类和信用卡类账户,并且对账户进行存取款操作,其中储蓄类账户每年一月一日结算,信用卡类账户每月一日结算。
一、头文件
1.date.h
该类用来记录程序中的时间,用户可以输入xxxx-xx-xx的格式,为其他类提供计算两日期间相差天数的方法。
#pragma once
#ifndef _DATE_H_
#define _DATE_H_
#include<iostream>
#include<utility>
#include<string>
using namespace std;
class Date {
private:
int year;
int month;
int day;
int totalDays;
public:
Date(int year, int month, int day);
Date() :year(0), month(0), day(0), totalDays(0) { ; }
inline int getYear() const { return year; }
inline int getMonth() const { return month; }
inline int getDay()const { return day; }
inline int getMaxDay() const;
bool isLeapYear() const;
void show() const;
int operator-(const Date& date) const {
return this->totalDays - date.totalDays;
}
bool operator< (const Date& date) const {
if (this->getYear() == date.getYear()) {
if (this->getMonth() == date.getMonth()) {
return this->getDay() < date.getDay();
}
return this->getMonth() < date.getMonth();
}
return this->getYear() < date.getYear();
}
friend istream& operator >> (istream& in, Date& date);
friend ostream& operator << (ostream& out, Date& date);
int getCurMonDay() const;
};
#endif
2.accumulator.h
该类是用来处理结算利息的辅助工具类。
#pragma once
#ifndef _ACCUMULATOR_H_
#define _ACCUMULATOR_H_
#include"date.h"
class Accumulator {
private:
Date lastDate;
double value;
double sum;
public:
Accumulator(Date date,double value);
double getSum(Date date) const;
void change(Date date, double value);
void reset(Date date, double value);
};
#endif
3.account.h
储蓄账户和信用卡账户的基类,其中有二者相同的元素。并在其中写入了Detail详情信息类,该类存储查询得到的相关信息,在multimap中存储用于使用时间范围查询流水信息,multimap以date为键详情信息为值,故在上述date类中需要重载<号以便满足键值对在map中有序。
#ifndef _ACCOUNT_H_
#define _ACCOUNT_H_
#include<iostream>
#include<map>
#include"date.h"
using namespace std;
class Detail {
private:
string id;
Date da;
double v, all;
string desc;
public:
Detail(string id,Date da, double v, double all, string desc) :id(id),da(da), v(v), all(all), desc(desc) {
}
void show() const{
cout << "账号:" << id << "\t";
da.show();
cout << "\t" << v << "\t" << all << "\t" << desc << endl;
}
};
typedef multimap<Date, Detail> RecordMap;
class Account {
private:
string id;
double balance;
static double total;
static RecordMap mp;
public:
Account(Date date, string id);
inline string getId() const { return id; }
inline double getBalance() const { return balance; }
inline static double getTotal() { return total; }
void record(Date date, double amount, const string& desc);
virtual void show() const {
cout << " " << getId() << "\t" << "Balance:" << getBalance() << endl;
};
virtual void deposit(Date date, double amount, const string& desc) = 0;
virtual void withdraw(Date date, double amount, const string& desc) = 0;
virtual void settle(Date date) = 0;
void error(string msg) const;
static void serach_find(const Date &date1, const Date &date2);
};
#endif
4.savingsaccount.h
该类是储蓄账户类,包含对储蓄账户的创建和存取款操作。
#pragma once
#ifndef _SAVINGSACCOUNT_H_
#define _SAVINGSACCOUNT_H_
#include<iostream>
#include"date.h"
#include"account.h"
#include"accumulator.h"
using namespace std;
class savingsAccount :public Account {
private:
Accumulator acc;
double rate;
public:
savingsAccount(Date date, string id, double rate);
double getRate() const { return rate; }
void deposit(Date date, double amount, const string &desc);
void withdraw(Date date, double amount, const string &desc);
void settle(Date date);
};
#endif
5.creditaccount.h
该类是信用卡账户类,包含对信用卡账户的创建和存取款操作。
#pragma once
#ifndef _CREDITACCOUNT_H_
#define _CREDITACCOUNT_H_
#include<iostream>
#include"account.h"
#include"accumulator.h"
#include"date.h"
using namespace std;
class creditAccount :public Account{
private:
Accumulator acc;
double credit, rate, fee;
double getDebt() const {
return getBalance() < 0 ? getBalance() : 0;
}
public:
creditAccount(Date date, string id, double credit, double rate, double fee);
double getCredit() const { return credit; }
double getRate() const { return rate; }
double getFee() const { return fee; }
double getAvailableCredit() const;
void deposit(Date date, double amount, const string &desc);
void withdraw(Date date, double amount, const string &desc);
void settle(Date date);
void show() const;
};
#endif
二、实现文件
1.date.cpp
其中totalDays的计算方法是,每次初始化对象时,计算当前存入日期到元年1月1日的日子数,365*(year-1)表示除当前年外所有年的日子总数(平年)然后加上这段时间出现的所有闰年数,(year-1)/4+(year-1)/400-(year-1)/100,普通年每四年一闰,世纪年每400年一闰,最后加上当前年的天数。
#include"date.h"
#include<cstdlib>
namespace {
const int DAYS_BEFORM_MONTH[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
const int DAYS[]{ 0,31,28,31,30,31,30,31,31,30,31,30,31 };
};
Date::Date(int year, int month, int day):year(year),month(month),day(day) {
if (isLeapYear() && month == 2) {
if (day <= 0 || day > 29) {
cout << "Invalid date:";
show();
cout << endl;
exit(1);
}
}
if (day <= 0 || day > DAYS[month]) {
cout << "Invalid date:";
show();
cout << endl;
exit(1);
}
totalDays = 365 * (year - 1) + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + getMaxDay() + day - 1;
}
int Date::getMaxDay() const {
if(isLeapYear()&&month>1)
return DAYS_BEFORM_MONTH[month]+1;
return DAYS_BEFORM_MONTH[month];
}
int Date::getCurMonDay() const {
return DAYS[this->month];
}
bool Date::isLeapYear() const {
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
return false;
}
void Date::show() const {
cout << year << "-" << month << "-" << day << "\t";
}
istream& operator >> (istream& in, Date& date) {
char ch;
in >> date.year ;
in >> ch;
in >> date.month;
in >> ch;
in >> date.day;
return in;
}
ostream& operator << (ostream& out, Date& date) {
out << date.year << "-" << date.month << "-" << date.day;
return out;
}
2.accumulator.cpp
在其他类中会经常调用以下函数。
#include"accumulator.h"
Accumulator::Accumulator(Date date, double value):lastDate(date),value(value),sum(0){
}
double Accumulator::getSum(Date date) const {
return sum + value * (date - lastDate);
}
void Accumulator::change(Date date, double value) {
sum = getSum(date);
lastDate = date;
this->value = value;
}
void Accumulator::reset(Date date, double value) {
lastDate = date;
this -> value = value;
sum = 0;
}
3.account.cpp
其中在账户信息发生变化时都会调用record函数,在该函数中向map加入每次变化的详情信息,方便以后查询。
#include"account.h"
double Account::total = 0;
RecordMap Account::mp ;
Account::Account(Date date, string id)
:id(id), balance(0) {
cout << date << "\t" << "#" << id << " is created " << endl;
}
void Account::record(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;
Account::mp.emplace(date, Detail(id,date,amount, balance,desc));
}
void Account::error(string msg) const {
cout << "当前错误信息为:" << msg << endl;
}
void Account::serach_find(const Date& date1, const Date& date2) {
auto start = Account::mp.lower_bound(date1);
auto over = Account::mp.upper_bound(date2);
for (; start != over; start++) {
start->second.show();
}
}
4.savingsaccount.cpp
该类是储蓄账户类,包含对储蓄账户的创建和存取款操作,储蓄账户每年一月一日结算利息。
#include"savingsaccount.h"
#include"accumulator.h"
savingsAccount::savingsAccount(Date date, string id, double rate) :Account(date,id), rate(rate),acc(Accumulator(date,0)) {
}
void savingsAccount::deposit(Date date, double amount, const string& desc) {
record(date, amount, desc);
acc.change(date, getBalance());
}
void savingsAccount::withdraw(Date date, double amount, const string& desc) {
if (amount <= getBalance()) {
record(date, -amount, desc);
acc.change(date, getBalance());
}
else {
cout << "您的账户余额不足!" << endl;
}
}
void savingsAccount::settle(Date date) {
const double interest = acc.getSum(date) * rate / 365;
if (interest != 0 && date.getMonth() == 1 && date.getDay() == 1) {
record(date, interest, "利息");
}
acc.reset(date, getBalance());
}
5.creditaccount.cpp
该类是信用卡账户类,包含对信用卡账户的创建和存取款操作,利息结算每月一次。
#include"creditaccount.h"
creditAccount::creditAccount(Date date, string id, double credit, double rate, double fee) :
Account(date, id), credit(credit), rate(rate), fee(fee), acc(Accumulator(date, 0)) {
}
double creditAccount::getAvailableCredit() const {
if (getBalance() < 0) {
return credit + getBalance();
}
return credit;
}
void creditAccount::deposit(Date date, double amount, const string& desc) {
record(date, amount, desc);
acc.change(date, getDebt());
}
void creditAccount::withdraw(Date date, double amount, const string& desc) {
if (amount <= getAvailableCredit()) {
record(date, -amount, desc);
acc.change(date, getDebt());
}
else {
cout << "余额不足" << endl;
}
}
void creditAccount::settle(Date date) {
const double interest = acc.getSum(date) * rate;
if (interest != 0 && getAvailableCredit() > -interest) {
record(date, interest, "利息");
}
if (date.getMonth() == 1 && getAvailableCredit() > fee) {
record(date, -fee, "卡费");
}
acc.reset(date, getBalance());
}
void creditAccount::show() const {
cout << " " << getId() << "\t" << "Balance:" << getBalance() << "\t" << "当前信用卡余额:" << getAvailableCredit() << endl;
}
三、main方法
在main方法中调用以上类进行测试,并将每次输入的命令存入txt文件中。
#include"account.h"
#include"savingsaccount.h"
#include"accumulator.h"
#include"date.h"
#include"creditaccount.h"
#include<vector>
#include<fstream>
using namespace std;
int main() {
ofstream fout("data.txt");
Date date(2008, 11, 1);
vector<Account*> accounts;
Account* acc;
std::cout << "操作:(a)增加用户,(d)存款,(w)取款,(s)查询账户信息,(c)快进,(n)进入下个月,(q)查询日期区间所有记录,(e)退出" << endl;
while (1) {
std::cout << date << "\t Total:" << Account::getTotal() << "\t command> ";
char str;
cin >> str;
if (str == 'a') {
char type;
string id;
cin >> type >> id;
if (type == 's') {
double rate;
cin >> rate;
acc = new savingsAccount(date, id, rate);
fout << str << " " << type << " " << id << " " << rate << endl;
}
else {
double credit, fee,rate;
cin >> credit >> rate >> fee;
acc = new creditAccount(date, id, credit, rate, fee);
fout << str << " " << type << " " << id << " " << credit << " " << rate << " " << fee << endl;
}
accounts.push_back(acc);
}
else if (str == 'd') {
int idx;
double amount;
string desc;
cin >> idx >> amount;
std::getline(cin, desc);
if (idx >= 0 && idx < accounts.size()) {
accounts[idx]->deposit(date, amount, desc);
}
else {
std::cout << "没有找到该账户,请重试" << endl;
}
fout << str << " " << idx << " " << amount << " " << desc << endl;
}
else if (str == 'w') {
int idx;
double amount;
string desc;
cin >> idx >> amount;
std::getline(cin, desc);
if (idx >= 0 && idx < accounts.size()) {
accounts[idx]->withdraw(date, amount, desc);
}
else {
std::cout << "没有找到该账户,请重试" << endl;
}
fout << str << " " << idx << " " << amount << " " << desc << endl;
}
else if (str == 's') {
for (int i = 0; i < accounts.size(); i++) {
std::cout << "[" << i << "]" << "\t";
accounts[i]->show();
}
}
else if (str == 'c') {
int day;
cin >> day;
if (day < date.getDay()) {
std::cout << "不能输入之前的日期" << endl;
}
else if(day>date.getCurMonDay()){
std::cout << "不能输入超过月份的日子" << endl;
}
else {
date = Date(date.getYear(), date.getMonth(), day);
}
fout << str << " " << day << endl;
}
else if (str == 'n') {
if (date.getMonth() == 12) {
date = Date(date.getYear() + 1, 1, 1);
}
else {
date = Date(date.getYear(), date.getMonth() + 1, 1);
}
for (auto it = accounts.begin(); it != accounts.end(); it++) (*it)->settle(date);
fout << str << endl;
}
else if (str == 'q') {
Date date1, date2;
cin >> date1 >> date2;
if (date2 < date1) {
std::cout << "第二个日期必须大于第一个日期,请重新输入" << endl;
}
else if (date1.getMonth() <= 0 || date1.getMonth() > 12 || date2.getMonth() <= 0 || date2.getMonth() > 12) {
std::cout << "请输入合法日期" << endl;
}
else if (date1.getDay() > date1.getCurMonDay() || date2.getDay() > date2.getCurMonDay()) {
std::cout << "请输入合法日期" << endl;
}
else {
Account::serach_find(date1, date2);
}
fout << str << " " << date1 << " " << date2 << endl;
}
else if (str == 'e') {
break;
}
}
fout.close();
std::cout << "结束时时间为:";
std::cout << date << endl;
std::cout << "Total: " << Account::getTotal() << endl;
return 0;
}
总结
对清华大学C++程序设计中的综合实践做一个总结,其中实现细节可能与课中展示有所不同,若发现其中存在问题欢迎指出一起讨论。最后的十一十二章内容没有全部实现,例如创建一个Date类对象失败程序怎么继续运行下去等异常处理问题。 计划下一步实现在Linux上搭建服务器接收Windows客户端传入的命令,然后服务端处理,最后返回结果在客户端上。 项目全部代码可以在Github仓库获取(欢迎start)!
|