This page last updated on: Mar. 16, 2002
A bank account transaction application is to be developed which performs nightly batch processing of daily transactions, as well as paying or charging interest to each bank account.
The application reads sets of data corresponding to each bank account, and transactions recorded for the day. Each transaction is posted against the appropriate account, then interest is calculated for each account (interest may be paid, or charged to the account).
After the transactions are posted and the daily interest calculated, each account is displayed, including the current balance.
Deposits (positive transactions) are added to the balance the same for each type of account. Withdrawals (negative transactions) can have different logic depending on the type of account, and interest may be paid or charged:
All account classes contain an account identification code (string) and a current balance (a generic Money type), along with operations to display the id and balance, and to provide withdrawals and deposits. The transactions identify the account (by account id) to apply either a deposit or a withdrawal.
Note that this assignment does not follow real banking practices, in many ways, and should not be used as a model for real financial applications.
Design an abstract base class to be used for all of the derived account classes. An abstract base class has at least one pure virtual function.
Common operations:
The application will take two command line arguments corresponding to two text files. The first text file will contain a list of account objects, with the data for each account stored in each line. The first token of the line will specify the account type: "Checking", "Savings", or "Credit". The rest of the line contains the account (string), the balance (Money type), and the yearly interest rate (double, in percentage form), if either a Savings or Credit account. For example:
Checking A-1111 100.0 Credit A-2222 200.0 11.5 Savings A-3333 1000.0 3.0
The second text file will contain a list of transactions to be applied against the account objects. The first entry of each line will be the account id, and the second will be a Money type either positive or negative. If positive, apply it as a deposit, otherwise as a withdrawal (note that this will need to be negated when calling the withdraw method). For example:
A-1111 -20.0 A-2222 300.0
The application will create a container of account objects (more specifically, it should be a container of base pointers to derived objects, to allow polymorphic operations against all of the entries in the container), apply each of the transactions against the objects, then apply the daily interest (for each appropriate account) and display each Account object.
Appropriate error checking and displays must be implemented, const safety used consistently, and efficiency concerns addressed (e.g. use references instead of by value where appropriate). Create a typedef for the Money type used in the account, and test it with double, at the minimum.
Name the source files acct.h, acct.cpp, and acctmain.cpp, and supply appropriate data files.
Extra credit: Templatize the Money type used in the Account and Transaction objects. A sample Money class has been created for use in testing the template classes (along with testing using fundamental types such as double). Rounding and truncation issues are always a concern, and the interest rate and interest rate calculations require special attention.
class SavingsAcct : public Acct {
Virtual functions need to be declared as virtual in the class declaration,
but not necessarily in the implementation file (most style guidelines suggest
declaring them virtual in both).
A virtual function with = 0; at the end of the function declares it to be pure:
virtual bool withdraw (Money amt) = 0; // pure virtualA pure virtual function defines the class declaration to be an abstract base class - no objects (instantiations) are allowed of that type, which implies that it is to be used only as a base class for derived types (which are typically called concrete types).
Base object initialization is performed through the initialization list, although the base class name is used rather than a object name, since there is not a variable name (only a class name) for the base object.
A base class will declare members as protected if it needs to be accessed by derived classes, but not by the clients (applications) of the base or derived classes:
protected: void setBalance(Money newBalance);The utility functions will only know of a base object reference or pointer and can only call base class member functions. At run-time, a base or derived object may be passed in to the utility function (by either pointer or reference) to be manipulated. The compiler takes care of the dynamic dispatching and interpretation of the actual object that is passed in to the utility function. New classes can be derived from any of these classes and the original utility function will work with all of them, invoking the appropriate withdraw function (for example). This means that the utility function can be written before other classes are inherited from a base or set of base classes. This also means that third-party software suppliers can write functions that invoke user-written virtual functions, without recompiling the original utility function (a re-link would be necessary).
The derived objects can be stored in a container of base pointers, such as:
std::vector<Acct*> accts;Polymorphic operations can then be applied against each object through the base pointer stored in the container. Note that the derived object will need to be allocated from the heap with the new operator, and then delete'd when no longer needed.
If a virtual function has been defined to provide std::ostream streaming, then operator<< only needs to be overloaded once:
virtual std::ostream& streamOut (std::ostream& ) const = 0; // pure virtual in base class
// ... each derived class implements streamOut
// implementation file (the declaration needs to be in the header):
std::ostream& operator<< (std::ostream& os, const Acct& acct) {
return acct.streamOut(os);
}
The std::ifstream class is recommended for use in reading and
parsing lines from the account and transaction files:
std::string acctType;
std::ifstream ifs(filename);
while (ifs >> acctType) { // extract first string, false if EOF
// ... extract more account info,
// ... 'new' derived object, push_back into container