//date.h#ifndef __DATE_H__#define __DATE_H__#include<iostream>classDate{//日期类private:int year;//年int month;//月int day;//日int totalDays;//该日期是从公元元年1月1日开始的第几天public:Date(int year =1,int month =1,int day =1);//用年、月、日构造日期intgetYear()const{return year;}intgetMonth()const{return month;}intgetDay()const{return day;}intgetMaxDay()const;//获得当月有多少天boolisLeapYear()const{//判断当年是否为闰年return year %4==0&& year %100!=0|| year %400==0;}//计算两个日期之间差多少天 intoperator-(const Date& date)const{return totalDays - date.totalDays;}//判断两个日期的前后顺序booloperator<(const Date& date)const{return totalDays < date.totalDays;}};
std::istream &operator>>(std::istream &in, Date &date);
std::ostream &operator<<(std::ostream &out,const Date &date);#endif//__DATE_H__
//date.cpp#include"date.h"#include<iostream>#include<stdexcept>usingnamespace std;namespace{//namespace使下面的定义只在当前文件中有效//存储平年中某个月1日之前有多少天,为便于getMaxDay函数的实现,该数组多出一项constint 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())throwruntime_error("Invalid date");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)return29;elsereturn DAYS_BEFORE_MONTH[month]- DAYS_BEFORE_MONTH[month -1];}
istream &operator>>(istream &in, Date &date){int year, month, day;char c1, c2;
in >> year >> c1 >> month >> c2 >> day;if(c1 !='-'|| c2 !='-')throwruntime_error("Bad time format");
date =Date(year, month, day);return in;}
ostream &operator<<(ostream &out,const Date &date){
out << date.getYear()<<"-"<< date.getMonth()<<"-"<< date.getDay();return out;}
//accumulator.h#ifndef __ACCUMULATOR_H__#define __ACCUMULATOR_H__#include"date.h"classAccumulator{//将某个数值按日累加private:
Date lastDate;//上次变更数值的时期double value;//数值的当前值double sum;//数值按日累加之和public://构造函数,date为开始累加的日期,value为初始值Accumulator(const Date &date,double value):lastDate(date),value(value),sum(0){}//获得到日期date的累加结果doublegetSum(const Date &date)const{return sum + value *(date - lastDate);}//在date将数值变更为valuevoidchange(const Date &date,double value){
sum =getSum(date);
lastDate = date;this->value = value;}//初始化,将日期变为date,数值变为value,累加器清零voidreset(const Date &date,double value){
lastDate = date;this->value = value;
sum =0;}};#endif//__ACCUMULATOR_H__
//account.h#ifndef __ACCOUNT_H__#define __ACCOUNT_H__#include"date.h"#include"accumulator.h"#include<string>#include<map>#include<istream>#include<stdexcept>classAccount;//前置声明classAccountRecord{//账目记录private:
Date date;//日期const Account *account;//账户double amount;//金额double balance;//余额
std::string desc;//描述public://构造函数AccountRecord(const Date &date,const Account *account,double amount,double balance,const std::string& desc);voidshow()const;//输出当前记录};//定义用来存储账目记录的多重映射类型typedef std::multimap<Date, AccountRecord> RecordMap;classAccount{//账户类private:
std::string id;//帐号double balance;//余额staticdouble total;//所有账户的总金额static RecordMap recordMap;//账目记录protected://供派生类调用的构造函数,id为账户Account(const Date &date,const std::string &id);//记录一笔帐,date为日期,amount为金额,desc为说明voidrecord(const Date &date,double amount,const std::string &desc);//报告错误信息voiderror(const std::string &msg)const;public:const std::string &getId()const{return id;}doublegetBalance()const{return balance;}staticdoublegetTotal(){return total;}//存入现金,date为日期,amount为金额,desc为款项说明virtualvoiddeposit(const Date &date,double amount,const std::string &desc)=0;//取出现金,date为日期,amount为金额,desc为款项说明virtualvoidwithdraw(const Date &date,double amount,const std::string &desc)=0;//结算(计算利息、年费等),每月结算一次,date为结算日期virtualvoidsettle(const Date &date)=0;//显示账户信息virtualvoidshow(std::ostream &out)const;//查询指定时间内staticvoidquery(const Date& begin,const Date& end);};inline std::ostream &operator<<(std::ostream &out,const Account &account){
account.show(out);return out;}classSavingsAccount:public Account {//储蓄账户类private:
Accumulator acc;//辅助计算利息的累加器double rate;//存款的年利率public://构造函数SavingsAccount(const Date &date,const std::string &id,double rate);doublegetRate()const{return rate;}virtualvoiddeposit(const Date &date,double amount,const std::string &desc);virtualvoidwithdraw(const Date &date,double amount,const std::string &desc);virtualvoidsettle(const Date &date);};classCreditAccount:public Account {//信用账户类private:
Accumulator acc;//辅助计算利息的累加器double credit;//信用额度double rate;//欠款的日利率double fee;//信用卡年费doublegetDebt()const{//获得欠款额double balance =getBalance();return(balance <0? balance :0);}public://构造函数CreditAccount(const Date &date,const std::string &id,double credit,double rate,double fee);doublegetCredit()const{return credit;}doublegetRate()const{return rate;}doublegetFee()const{return fee;}doublegetAvailableCredit()const{//获得可用信用if(getBalance()<0)return credit +getBalance();elsereturn credit;}virtualvoiddeposit(const Date &date,double amount,const std::string &desc);virtualvoidwithdraw(const Date &date,double amount,const std::string &desc);virtualvoidsettle(const Date &date);virtualvoidshow(std::ostream &out)const;};classAccountException:public std::runtime_error {private:const Account *account;public:AccountException(const Account *account,const std::string &msg):runtime_error(msg),account(account){}const Account *getAccount()const{return account;}};#endif//__ACCOUNT_H__
//account.cpp#include"account.h"#include<cmath>#include<iostream>#include<utility>usingnamespace std;usingnamespace std::rel_ops;//AccountRecord类的实现
AccountRecord::AccountRecord(const Date &date,const Account *account,double amount,double balance,const std::string& desc):date(date),account(account),amount(amount),balance(balance),desc(desc){}void AccountRecord::show()const{
cout << date <<"\t#"<< account->getId()<<"\t"<< amount <<"\t"<< balance <<"\t"<< desc << endl;}//Account类的实现double Account::total =0;
RecordMap Account::recordMap;
Account::Account(const Date &date,const string &id):id(id),balance(0){
cout << date <<"\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;
AccountRecord record(date,this, amount, balance, desc);
recordMap.insert(make_pair(date, record));
record.show();}void Account::show(ostream &out)const{
out << id <<"\tBalance: "<< balance;}void Account::error(const string &msg)const{throwAccountException(this, msg);}void Account::query(const Date& begin,const Date& end){if(begin <= end){
RecordMap::iterator iter1 = recordMap.lower_bound(begin);
RecordMap::iterator iter2 = recordMap.upper_bound(end);for(RecordMap::iterator iter = iter1; iter != iter2;++iter)
iter->second.show();}}//SavingsAccount类相关成员函数的实现
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){if(date.getMonth()==1){//每年的一月计算一次利息double interest = acc.getSum(date)* rate
/(date -Date(date.getYear()-1,1,1));if(interest !=0)record(date, interest," interest");
acc.reset(date,getBalance());}}//CreditAccount类相关成员函数的实现
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(ostream &out)const{
Account::show(out);
out <<"\tAvailable credit:"<<getAvailableCredit();}