1. 程式人生 > >【C++綜合例項】陣列指標字串以及繼承派生:銀行賬戶管理系統

【C++綜合例項】陣列指標字串以及繼承派生:銀行賬戶管理系統

一個活期儲蓄賬戶包括:

資訊:賬號(id)、餘額(balance)、年利率(rate)等

操作:顯示賬戶資訊(show)、存款(deposit)、取款(withdraw)、結算利率(settle)等。

為此,我們定義了一個SavingAccount類:一開始id和日期date都是使用簡單的int型別,後面會修改。

在UML建模中,“+”表示成員是共有的,“-”表示成員是私有的

無論是存款,取款還是結算利息,都需要修改當前的餘額並且將餘額的變動輸出。我麼定義一個record私有成員來執行。所有的日期都是以日為單位的相對日期,這樣的話,通過兩個日期相減既可以得到兩個日期的相差天數,在計算利息時很方便。

難點1、利息的計算。因為餘額是變化的。

那麼,現在的類結構如下圖所示:

版本一的不足之處:

     

改進1:

為了解決其中的一部分問題,為SavingAccount類增加一個靜態資料成員total:double,其好處是在於全體賬戶物件共享一份,技能節省儲存空間,有無須擔心資料一致性問題。

另外,將不需要改變的成員函式,生命為長成員函式,用const修飾。

相應的,新增靜態成員函式getTotal()函式來訪問total成員。

改進2:

日期使用整數雖然方便,但是不直觀也不友好。所以我們可以使用一個類來表示日期,內涵年月日三個資料成員。但是這就給日期的計算帶來了不變。最後,我們使用了“相對日期”來計算兩個日期的相差天數,只需要將兩個日期的相對日期相減就可以了。

將公元元年1月1日定為公共的基準日期,將y年m月d日相聚這個基準日期記作f(y/m/d,1/1/1)可以將其分解為三部分:

f(y/m/d.1/1/1)=f(y/1/1,1/1/1) + f(y/m/1,y/1/1) + f(y/m/d,y/m/1)

其中的第一部分:f(y/1/1,1/1/1),元年每年365天,元年數加上閏年數。閏年數的計算:4年一閏,100的倍數免閏,400的倍數再閏。

所以:f(y/1/1,1/1/1)=(y-1)*365 + (y-1)/4 - (y-1)/100 + (y-1)/400

最簡單的是f(y/m/d,y/m/1) = (d-1)

中間部分比較難以表示為一個統一的公式,但是平年中指定月份的1日與1月1日相差天數可以由月份唯一確定,所以可以把每月1日到1月1日的天數放在一個數組中,計算式只需要查詢該陣列,便可得到f(y/m/1,1/1/1).而對於閏年,只需要將m>2時差的的值加上1,該值只依賴m和y,我們把它記成g(m,y)。

將公元元年1月1日的相對日期定為i,則y年m月d日的相對日期就是:

f(y/m/d,1/1/1)=(y-1)365 + (y-1)/4 - (y-1)/100 + (y-1)/400 +g(m,y) + d

Date類:

改進剩餘部分:用整數表示銀行賬號不完美,比如有時賬號以0開頭,或者賬號超過整數的表示範圍或者賬號中有其他字串,所以整數的表示方式不能勝任,我們將賬號型別改為字串形式。

賬目列表使用字串為各筆賬目增加說明性文字

專門增加一個函式用於報告錯誤資訊,當其他函式需要輸出錯誤資訊時,直接把資訊以字串形式傳遞給該函式即可,簡化了錯誤資訊的輸出。

利用陣列避免程式碼冗餘:之前建立賬戶時,兩個賬戶都是獨立的變數,只能用名字取引用他們,主程式末尾分別對兩個賬戶進行結算(settle)和顯示(show)時需要將幾乎相同的程式碼書寫兩遍,如果賬戶數量增多將會帶來更多的麻煩。所以可以將多個賬戶組織在一個數組中,這樣可以把需要對各個賬戶做得事情放在迴圈中,避免了程式碼的冗餘。

修改後的程式碼:

//date.h
using namespace std;
//日期類
class Date
{
public:
	Date(int year, int month, int day);
	int getYear() const{ return year; }//const成員函式(位置在函式之後),不能修改
	int getMonth() const{ return month; }
	int getDay() const { return day; }
	int getMaxDay() const;//獲得當月有多少天
	bool isLeapyYear() const{//判斷當年是否為閏年
		return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
	}
	void show() const;//輸出當前日期
	//計算兩個日期之間差多少天
	int distance(const Date& date) const {
		return totalDays - date.totalDays;
	}

private:
	int year;
	int month;
	int day;
	int totalDays;//該日期是從公元1月1日開始的第幾天
};

//date.cpp

#include<iostream>
#include<cmath>
#ifndef _DATA_H
#define _DATA_H
#include"date.h"
#endif // !_DATA_H


#include<cstdlib>//exit函式,該函式的宣告在cstdlib標頭檔案中
using namespace std;

namespace {//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 (isLeapyYear() && month > 2) totalDays++;
}

int Date::getMaxDay() const{
	if (isLeapyYear() && month == 2)
		return 29;
	else
		return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
}
void Date::show() const {
	cout << getYear() << "-" << getMonth() << "-" << getDay();
}


//account.h

#ifndef _DATA_H
#define _DATA_H
#include"date.h"
#endif // _DATA_H

#pragma once
#include<iostream>
#include<cmath>
#include<string>


using namespace std;

class SavingAcount//儲蓄賬戶類
{

private:
	string id;//賬號
	Date lastDate;//上次變更日期
	double balance;//餘額
	double accumulation;//餘額按照日累加之和
	double rate;//利率
	static double total;
	//記每一筆賬,date為日期,amount為金額,desc為說明
	void record(const Date &date, double amount, const string &desc);
	//獲得到指定日期為止的存款金額按日累計值
	double accumulate(const Date& date)const {
		return accumulation + balance * date.distance(lastDate);
	}

public:
	//建構函式
	SavingAcount(const Date& date,const string& id, double rate);
	~SavingAcount();
	string getId() const { return id; }
	double getBalance() const { return balance; }
	double getRate() const { return rate; }
	static double getTotal() { return total; }
	//顯示賬戶資訊
	void show() const;
	//存入現金
	void deposit(const Date& date, double amount,const string &desc);
	//取出現金
	void withdraw(const Date &date, double amount, const string &desc);
	void error(const string & msg);
	//結算利息
	void settle(const Date& date);
};


//account.cpp

#include<iostream>
#include<cmath>

#ifndef _ACCOUNT_H
#define _ACCOUNT_H
#include"account.h"
#endif // !_ACCOUNT_H


using namespace std;

double SavingAcount::total = 0;
//SavingsAccount類相關成員函式的實現
SavingAcount::SavingAcount(const Date &date, const string &id, double rate)
	: id(id), balance(0), rate(rate), lastDate(date), accumulation(0) {
	date.show();
	cout << "\t#" << id << " created" << endl;
}

SavingAcount::~SavingAcount()
{
}

void SavingAcount::record(const Date &date, double amount, const string &desc) {
	accumulation = accumulate(date);
	lastDate = date;
	amount = floor(amount * 100 + 0.5) / 100;  //保留小數點後兩位
	balance += amount;
	total += amount;
	date.show();
	cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}


void SavingAcount::settle(const Date& date) {
	double interest = accumulate(date) * rate	//計算年息
		/ date.distance(Date(date.getYear() - 1, 1, 1));
	if (interest != 0)
		record(date, interest, "interest");
	accumulation = 0;
}


void SavingAcount::deposit(const Date &date, double amount,const string &desc) {
	record(date, amount, desc);
}

void SavingAcount::withdraw(const Date &date, double amount, const string &desc) {
	if (amount > getBalance())
		cout << "Error: not enough money" << endl;
	else
		record(date, -amount, desc);
}

void SavingAcount::show() const{
	cout << "#" << id << "\tBalance:" << balance;
}

void SavingAcount::error(const string &msg) {
	cout << "Error(#" << id << "): " << msg << endl;
}


//main.cpp

#include<iostream>
#include<cmath>
#ifndef _ACCOUNT_H
#define _ACCOUNT_H
#include"account.h"
#endif // !_ACCOUNT_H


using namespace std;

int main() {
	Date date(2018, 9, 1);	//起始日期
							//建立幾個賬戶
	SavingAcount accounts[] = {
		SavingAcount(date, "S3755217", 0.015),
		SavingAcount(date, "02342342", 0.015)
	};
	const int n = sizeof(accounts) / sizeof(SavingAcount); //賬戶總數
															 //11月份的幾筆賬目
	accounts[0].deposit(Date(2018, 11, 5), 5000, "salary");
	accounts[1].deposit(Date(2018, 11, 25), 10000, "sell stock 0323");
	//12月份的幾筆賬目
	accounts[0].deposit(Date(2018, 12, 5), 5500, "salary");
	accounts[1].withdraw(Date(2018, 12, 20), 4000, "buy a laptop");

	//結算所有賬戶並輸出各個賬戶資訊
	cout << endl;
	for (int i = 0; i < n; i++) {
		accounts[i].settle(Date(2019, 1, 1));
		accounts[i].show();
		cout << endl;
	}
	cout << "Total: " << SavingAcount::getTotal() << endl;
	return 0;
}

另外,還可以對程式進行擴充套件,以支援信用卡使用者。

*************後續再補充*************

*************2018/9/26**************

*****加入了繼承和派生的概念*****

**************************************

信用卡賬戶業務特點:

允許透支及信用額度:總的透支金額應該在這個額度之內

利息:信用賬戶中存錢不會有利息,二使用信用賬戶透支只需要支付利息,信用賬戶的利率一般以日為單位,為了簡單起見,我們不去考慮這個免息期。

每月結算:與儲蓄賬戶每年結算一次利息不同,信用賬戶每月結算一次,我麼假定結算日是每月的1日

年費:此外,信用賬戶每年需要交一次年費,在本例中我們認為在每年的1月1日結算的時候扣繳年費。

結構調整:

設計一個基類Account用來描述所有賬戶的共性,再從中派生出:SavingAccount類和CreditAccount類

兩類賬戶的利息計算有很大的差異:無論是計息的物件還是計息的週期。因此,計息任務也不能由基類Account來完成。

然而,兩類賬戶在計算利息時都需要將某個數值(餘額或者欠款金額)按日累加,為了避免書寫重複的程式碼,有兩種可行的解決方法:

  1. 在基類Account中設立幾個額保護的成員函式來協助利息計算,然後在派生類中通過呼叫這些函式來計算利息。
  2. 建立一個新的類,由該類提供計算一項數值的按日累加之和所需要的介面,在兩個派生類中分別將該類例項化,通過該類的實力來計算利息。

由於計算一項數值的按日累加之和這個功能是與其他的賬戶管理功能相對獨立的,因此將這項功能從賬戶類中分離出來更好。這樣可以降低賬戶類的複雜性,提高計算數值的按日累加之和程式碼的可複用性。

以下是相應的UML結構:

                                                         

                               

以下是程式碼:(明天碼碼看能不能搞出來)