Title: Extending the Class Capabilities
1Extending the Class Capabilities
Lets extend the definition of the BankAccount
class to illustrate some additional capabilities
provided by C classes.
//////////////////////////////////////////////////
////////////////////// // BankAcct.h
// //
The class definition for the "NEW AND IMPROVED"
BankAccount class. // ////////////////////////////
//////////////////////////////////////////// ifnd
ef BANK_ACCOUNT_H using namespace std const int
ACCOUNT_NBR_LENGTH 13 const int PIN_LENGTH
7 class BankAccount public //
Constructors BankAccount()
BankAccount(string newAccountNumber, string
newPIN, float newBalance,
float newInterestRate) BankAccount(const
BankAccount account) // Member
Functions void setBalance(float
newBalance) void setInterestRate(float
newInterestRate) bool checkAccountNumber(st
ring testAccountNumber) bool
checkPIN(string testPIN) void
deposit(float amount) void withdraw(float
amount)
const modifier
2BankAcct.h (Continued)
const modifiers
// Accessor Functions bool getActive()
const float getBalance() const float
getInterestRate() const // Binary
Operators bool operator (const BankAccount
account) const bool operator lt (const
BankAccount account) const bool operator lt
(const BankAccount account) const bool
operator gt (const BankAccount account) const
bool operator gt (const BankAccount account)
const bool operator ! (const BankAccount
account) const // Friend Functions
friend void merge(BankAccount accountA,
BankAccount accountB) friend istream
operator gtgt (istream sourceFileStream,
BankAccount account)
friend ostream operator ltlt (ostream
destinationFileStream,
const BankAccount account)
overloaded operators
friend functions
3BankAcct.h (Continued)
private // Member Functions void
setActive(bool newActive) void
setAccountNumber(string newAccountNumber)
void setPIN(string newPIN) // Accessor
Functions string getAccountNumber() const
string getPIN() const // Data
Members string accountNumber bool
active string pin float balance
float interestRate define
BANK_ACCOUNT_H endif
const modifiers
Well examine the const modifiers, overloaded
operators, and friend functions as we come to
them in the BankAccounts implementation file and
in its driver program.
4BankAcct.cpp
//////////////////////////////////////////////////
//////////////////////// // BankAcct.cpp
// //
// // The class
implementation for the NEW AND IMPROVED
BankAccount class. // ////////////////////////////
////////////////////////////////////////////// in
clude ltiomanipgt include ltfstreamgt include
ltcstringgt include "BankAcct.h" using namespace
std // Default constructor. Initializes no
data members. // BankAccountBankAccount() /
/ Initializing constructor. Initializes
appropriate data members // // to parameterized
values initializes active data member to true.
// BankAccountBankAccount(string
newAccountNumber, string newPIN,
float newBalance, float
newInterestRate) setAccountNumber(newAccountN
umber) setPIN(newPIN) setBalance(newBalanc
e) setInterestRate(newInterestRate)
setActive(true)
5BankAcct.cpp (Continued)
// Copy constructor. Duplicates the
parameterized BankAccount. // BankAccountBankAcc
ount(const BankAccount account) string
acctNbr string pinNbr account.getAccountN
umber(acctNbr) setAccountNumber(acctNbr)
setActive(account.getActive()) if (active)
account.getPIN(pinNbr)
setPIN(pinNbr) setBalance(account.getBalanc
e()) setInterestRate(account.getInterestRat
e()) return
const modifier
Note that the parameter is sent by
constant-reference, meaning that no duplicate is
made, but that the member function must treat the
parameter as a constant (i.e., it cannot alter
any aspect of it).
Since the member functions getAccountNumber,
getActive, getPIN, getBalance, and
getInterestRate are all applied to the account
parameter, the compiler must be made aware that
these member functions cannot alter the
BankAccount to which they are applied. Well see
how this is done when we get to
the implementations of those member functions.
6BankAcct.cpp (Continued)
// Member function to set the balance data member
// // to the parameterized value (if
appropriate). // void BankAccountsetBalance(fl
oat newBalance) if (newBalance gt 0.0F)
balance newBalance return // Member
function to set the interestRate data member
// // to the parameterized value (if
appropriate). // void BankAccountsetInter
estRate(float newInterestRate) if
(newInterestRate gt 0.0F) interestRate
newInterestRate return // Member function
to verify whether the accountNumber data // //
member has the same value as the parameterized
value. // bool BankAccountcheckAccountNumber(
string testAccountNumber) return
(accountNumber testAccountNumber)
7BankAcct.cpp (Continued)
// Member function to verify whether the pin data
member // // has the same value as the
parameterized value. // bool
BankAccountcheckPIN(string testPIN) return
(pin testPIN) // Member function to
increment the balance data // // member by
the parameterized value (if appropriate). // void
BankAccountdeposit(float amount) if
(amount gt 0.0F) balance amount //
Member function to decrement the balance data
// // member by the parameterized value (if
appropriate). // void BankAccountwithdraw(float
amount) if (amount lt balance) balance
- amount return
8BankAcct.cpp (Continued)
// Accessor member function for the active data
member. // bool BankAccountgetActive() const
return active // Accessor member function
for the balance data member. // float
BankAccountgetBalance() const return
balance // Accessor member function for the
interestRate data member. // float
BankAccountgetInterestRate() const return
interestRate
const modifiers
By making these member functions constant, the
compiler has been assured that they do not alter
the associated BankAccount. If their code did try
to alter the BankAccount, either directly or by
invoking a non-constant member function, then the
compilation would be halted and the code would
have to be altered.
9BankAcct.cpp (Continued)
// Equality operator. All inactive accounts are
considered equal // // active accounts with the
same balance and interest rate are also // //
considered equal otherwise, accounts are not
considered equal. // bool BankAccountoperator
(const BankAccount account) const if
((!active) (!account.active)) return
true else if ((!active) (!account.active))
return false else return
((interestRate account.interestRate)
(balance account.balance))
overloaded operator
Several C operators (, lt, lt, gt, gt, !, ,
, -, , /, , , --, , -, , etc.) can be
overloaded to apply to new classes. In this case,
the equality operator has been overloaded for the
BankAccount class, making it possible to test the
equality of two BankAccount variables x and y
with a simple statement like if (x y)
... Note that BankAccount equality has been
defined very specifically. Also, notice that the
parameter has been sent by constant-reference and
that the operator has a const modifier, so
neither BankAccount can be altered by the
equality operator.
10BankAcct.cpp (Continued)
// Less-than operator. An inactive account is
considered less than any // // active account an
active account is considered less than another
// // active account if it has a smaller interest
rate or, if they have // // the same interest
rate and it has a smaller balance otherwise, an
// // account is not considered less than
another account. // bool
BankAccountoperator lt (const BankAccount
account) const if (!account.active)
return false else if (!active) return
true else if (interestRate lt
account.interestRate) return true else
if (interestRate gt account.interestRate)
return false else return (balance lt
account.balance) // Less-than-or-equal
operator. An account is considered // // less
than or equal to another account if it is either
// // less than (lt) or equal to () the other
account. // bool BankAccountoperator lt
(const BankAccount account) const return
((this lt account) (this account))
overloaded operators
Notice that the lt operator has been defined to
take advantage of the previously defined lt and
operators. Also, observe the use of this when
referring to the calling object, in this case,
the first BankAccount in the comparison.
11BankAcct.cpp (Continued)
// Greater-than operator. An account is
considered greater than another // // account if
the second account is less than (lt) the first
account. // bool BankAccountoperator gt
(const BankAccount account) const return
(account lt this) // Greater-than-or-equal
operator. An account is considered // // greater
than or equal to another account if the second
// // account is less than or equal to (lt) the
first account. // bool BankAccountoperator gt
(const BankAccount account) const return
((this gt account) (this account)) //
Inequality operator. An account is considered
unequal to another // // account if the account
is not equal to () the other account.
// bool BankAccountoperator ! (const
BankAccount account) const return !(this
account)
overloaded operators
Again, each of these operators has been defined
to take advantage of the previously defined
operators. Also, observe that its okay to
reverse the roles of this and the parameter
account in the gt operators definition, since
both BankAccount values have been subjected to a
const modifier.
12BankAcct.cpp (Continued)
// Friend function to implement the input
operator for BankAccounts. It is // // assumed
that all BankAccount input will be formatted in
the specified // // order the accountNumber,
followed by a character that indicates whether
// // the BankAccount is active ('Y' means it
is). If active, this character // // is
followed by the PIN, the balance, and the
interest rate. // istream operator
gtgt (istream sourceFileStream, BankAccount
account) string accountNbr char
activeIndicator string pinNbr float bal
float intRate sourceFileStream gtgt
accountNbr account.setAccountNumber(accountNbr
) sourceFileStream gtgt activeIndicator if
(activeIndicator ! 'Y') account.setActive(f
alse) else account.setActive(true)
sourceFileStream gtgt pinNbr
account.setPIN(pinNbr) sourceFileStream gtgt
bal account.setBalance(bal)
sourceFileStream gtgt intRate
account.setInterestRate(intRate) return
sourceFileStream
friend function
A friend function is not a member function of the
class, but its friend designation does give it
access to the private members of the class. While
this tends to violate our information hiding
principles, it does make it possible to implement
fancy functions like this input operator, which
can read a properly formatted BankAccount from an
input stream. Note that friend functions may be
implemented in either the class implementation or
in the driver, but they must be declared as
friends in the class definition itself. This
affords the class at least a modicum of
protection.
13BankAcct.cpp (Continued)
// Friend function to implement the output
operator for BankAccounts. Only // // the
accountNumber and an inactive message is output
for inactive accounts // // an active account
has its PIN, balance, and interestRate output as
well. // ostream operator ltlt (ostream
destinationFileStream, const BankAccount
account) destinationFileStream.setf(iosfix
ed) destinationFileStream ltlt
setprecision(2) string accountNbr
account.getAccountNumber(accountNbr)
destinationFileStream ltlt "Account Number " ltlt
accountNbr ltlt endl if (account.getActive()
false) destinationFileStream ltlt "
ACCOUNT INACTIVE " else string
pinNbr account.getPIN(pinNbr)
destinationFileStream ltlt "PIN " ltlt
pinNbr ltlt endl destinationFileStream ltlt
"Interest Rate " ltlt
100account.getInterestRate() ltlt '' ltlt endl
destinationFileStream ltlt "Current Balance "
ltlt account.getBalance() return
destinationFileStream
friend function
This friend function implements the output
operator for the BankAccount class. Notice how
dangerous this is while the accessor functions
for the accountNumber and pin data members were
private, this operator is public, permitting any
driver to output that information! It would
probably be smarter to implement this friend
function in a driver where this kind of output
operator would be appropriate.
14BankAcct.cpp (Continued)
// Member function to set the active data member
to the parameterized value. // void
BankAccountsetActive(bool newActive)
active newActive return // Member
function to set the accountNumber data member to
the parameterized value. // void
BankAccountsetAccountNumber(string
newAccountNumber) accountNumber
newAccountNumber return // Member
function to set the pin data member to the
parameterized value. // void BankAccountsetPIN(s
tring newPIN) pin newPIN return
15BankAcct.cpp (Continued)
// Accessor member function for the accountNumber
data member. // string BankAccountgetAccountNumb
er() const return accountNumber //
Accessor member function for the pin data member.
// void BankAccountgetPIN() const return
pin
const modifiers
Finally, the last two accessor functions for the
BankAccount class have const modifiers to assure
the compiler that they dont alter the calling
object, the BankAccount this.
To complete our introduction to these new
features, well next examine a driver program to
test the BankAccount class.
16BankManagerDriver.cpp
//////////////////////////////////////////////////
//// // BankManagerDriver.cpp
// //
// // This program permits a bank
manager to retrieve, // // peruse, and alter a
list of BankAccount data. // //////////////////
//////////////////////////////////// include
ltiostreamgt include ltfstreamgt include
ltcstdlibgt include ltstringgt include
"BankAcct.h" using namespace std const int
MAX_ACCOUNT_CAPACITY 16 void
loadAccounts(BankAccount list, int
length) char queryUser() void
mergeTwoAccounts(BankAccount list, int
length) void openNewAccount(BankAccount list,
int length) void updateAccount(BankAccount
list, int length) void viewAccount(BankAccount
list, int length) int indexOfAccount(string
acctNbr, BankAccount list, int length)
Note that the prototype for the one remaining
friend function of the BankAccount class, merge,
is not included in the drivers list of
prototypes, in spite of the fact that merge will
actually be implemented in this driver. That is
because that prototype has already been defined
in BankAcct.h, the BankAccount class definition
file.
17BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
//////////////////// // The main function drives
the bank manager's interactive session.
// ///////////////////////////////////////////////
/////////////////////// void main()
BankAccount acctListMAX_ACCOUNT_CAPACITY
int listLength char request
loadAccounts(acctList, listLength) request
queryUser() while (request ! 'Q')
switch (request) case 'M'
mergeTwoAccounts(acctList, listLength) break
case 'N' openNewAccount(acctList,
listLength) break case 'U'
updateAccount(acctList, listLength) break
case 'V' viewAccount(acctList, listLength)
break request queryUser()
return
18BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
/////////////////////////////// // The merge
function is a friend function to the BankAccount
class. It // // combines the balances of
two active BankAccounts into the BankAccount with
// // the higher interest rate (or, if the
interest rates are the same, the Bank- // //
Account with the higher initial balance), and
closes the other BankAccount. // /////////////////
//////////////////////////////////////////////////
////////////// void merge(BankAccount accountA,
BankAccount accountB) if (accountA.accountNu
mber accountB.accountNumber) return
if ((accountA.active) (accountB.active))
if (accountA gt accountB)
accountA.deposit(accountB.balance)
accountB.setActive(false) else
accountB.deposit(accountA.balance)
accountA.setActive(false)
return
friend function
Here is the other friend function that was
defined back in BankAcct.h. Note that, unlike
regular functions defined in the driver, this
function has access to every private member of
the BankAccount class!
19BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
//////////////////////////////// // The
loadAccounts function queries the user for the
name of a file containing // // BankAccount
information, opens that file, and extracts the
BankAccount data, // // placing it into the
parameterized array of BankAccounts.
// ////////////////////////////////////////
////////////////////////////////////////// void
loadAccounts(BankAccount list, int length)
ifstream bankFile string bankFileName
cout ltlt "Enter the name of the file containing
the bank account data " cin gtgt
bankFileName bankFile.open(bankFileName.c_str(
)) bankFile gtgt length for (int i 0 i
lt length i) bankFile gtgt listi
bankFile.close()
20BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
////////////////////////// // The queryUser
function asks the user to input a letter
indicating what // // BankAccount operation
should be performed next. After the user enters
// // a valid response, that letter is returned
to the calling function. // //////////////////
//////////////////////////////////////////////////
//////// char queryUser() char ltr cout
ltlt "Select the letter corresponding to the
function you wish to perform ltlt endl ltlt
endl cout ltlt '\t' ltlt "U - Update an existing
account" ltlt endl ltlt '\t' ltlt "V - View an
existing account" ltlt endl ltlt '\t' ltlt "N -
Open a new account" ltlt endl ltlt '\t' ltlt "M
- Merge two existing accounts" ltlt endl ltlt
'\t' ltlt "Q - Quit processing bank accounts" ltlt
endl ltlt endl ltlt "SELECT ONE LETTER NOW
" cin gtgt ltr ltr toupper(ltr) while
((ltr ! 'U') (ltr ! 'V') (ltr ! 'N')
(ltr ! 'M') (ltr ! 'Q')) cout ltlt
"You must choose one of the letters listed above.
Try again " cin gtgt ltr ltr
toupper(ltr) return ltr
21BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
///////////////////////////////// // The
mergeTwoAccounts function queries the user for
the account numbers of the // // two BankAccounts
that are to be combined. If BankAccounts with
these numbers // // reside in the parameterized
list, then the accounts are appropriately merged.
// ///////////////////////////////////////////////
//////////////////////////////////// void
mergeTwoAccounts(BankAccount list, int
length) string acctNbr BankAccount
dummyAcct int index2 for (int i 0 i
lt 1 i) if (i 0) cout
ltlt "Enter the account number of the first
account " else cout ltlt "Enter
the account number of the second account "
cin gtgt acctNbr indexi
indexOfAccount(acctNbr, list, length) if
(indexi lt 0) cout ltlt "NO
ACCOUNT WITH THAT NUMBER. REQUEST DENIED." ltlt
endl ltlt endl return
merge(listindex0, listindex1) cout ltlt
"ACCOUNT MERGER PROCESSED." ltlt endl ltlt endl
return
22BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
/////////////////////////////////// // The
openNewAccount function queries the user for the
account number to be // // given to a brand
new BankAccount. If that number doesn't already
belong to // // one of the BankAccounts
residing in the parameterized list, then a new
// // BankAccount is created with the
user-supplied account number, PIN, balance,
// // and interest rate, and that BankAccount is
added to the list (if there's room).
// ///////////////////////////////////////////////
////////////////////////////////////// void
openNewAccount(BankAccount list, int
length) string newAcctNbr string
newPIN float newBal float newIntRate
int index if (length MAX_ACCOUNT_CAPACITY)
cout ltlt "NO ROOM FOR MORE ACCOUNTS.
REQUEST DENIED." ltlt endl ltlt endl return
cout ltlt "Enter the account number for the
new account " cin gtgt newAcctNbr index
indexOfAccount(newAcctNbr, list, length) if
(index gt 0) cout ltlt "DUPLICATE
ACCOUNT NUMBER. REQUEST DENIED." ltlt endl ltlt
endl return
23BankManagerDriver.cpp (Continued)
cout ltlt "Enter the PIN for the new account
" cin gtgt newPIN cout ltlt "Enter the initial
balance for the new account " cin gtgt
newBal cout ltlt "Enter the interest rate for the
new account " cin gtgt newIntRate BankAccount
newAcct(newAcctNbr, newPIN, newBal,
newIntRate) listlength newAcct length
cout ltlt "NEW ACCOUNT PROCESSED." ltlt endl ltlt
endl return
24BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
//////////////////////// // The updateAccount
function queries the user for the account number
// // of a BankAccount whose balance is to be
altered. If that number // // corresponds to
one of the BankAccounts residing in the
parameterized // // list, then the user is asked
to supply the amount to be deposited or // //
withdrawn from the account, and the transaction
is properly handled. // //////////////////////////
//////////////////////////////////////////////// v
oid updateAccount(BankAccount list, int
length) string acctNbr int index
char request float amt cout ltlt "Enter
the account number of the account being updated
" cin gtgt acctNbr index
indexOfAccount(acctNbr, list, length) if
(index lt 0) cout ltlt "NO ACCOUNT WITH
THAT NUMBER. REQUEST DENIED." ltlt endl ltlt endl
return
25BankManagerDriver.cpp (Continued)
cout ltlt "Enter \"D\" for deposit or \"W\" for
withdrawal " cin gtgt request request
toupper(request) while ((request ! 'D')
(request ! 'W')) cout ltlt "You must
enter \"D\" or \"W\". Please try again "
cin gtgt request request
toupper(request) cout ltlt "Enter the
amount " cin gtgt amt if (request
'D') listindex.deposit(amt) else
listindex.withdraw(amt) cout ltlt "ACCOUNT
UPDATE PROCESSED." ltlt endl ltlt endl return
26BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
////////////////////////////////////// // The
viewAccount function queries the user for the
account number of a BankAccount // // whose data
is to be output. If that number corresponds to
one of the BankAccounts // // residing in the
parameterized list, then BankAccount is
appropriately output. // ////////////////////
//////////////////////////////////////////////////
////////////////// void viewAccount(BankAccount
list, int length) string acctNbr int
index cout ltlt "Enter the account number of
the account to be displayed " cin gtgt
acctNbr index indexOfAccount(acctNbr, list,
length) if (index lt 0) cout ltlt "NO
ACCOUNT WITH THAT NUMBER. REQUEST DENIED." ltlt
endl ltlt endl return cout ltlt
listindex ltlt endl ltlt endl return
27BankManagerDriver.cpp (Continued)
//////////////////////////////////////////////////
/////////////////////////////// // The
indexOfAccount function searches the
parameterized list of BankAccounts // // for one
with the parameterized account number. If one is
found, its list // // index is returned
otherwise, a value of -1 is returned.
// ////////////////////////////////////////
///////////////////////////////////////// int
indexOfAccount(string acctNbr, BankAccount
list, int length) for (int i 0 i lt
length i) if (listi.checkAccountNu
mber(acctNbr)) return i return
-1
Now lets test the BankAccount class and this new
driver.
28Testing the View Function Active Inactive
29Testing the View Function Bad Good Account
Numbers
30Testing the Merge Function
31Testing the Update Function Withdrawing Funds
32Testing the Update Function Withdrawing Too Much
33Testing the Update Function Depositing
34Testing the Update Function Depositing a Negative
35Testing the New Account Function An Unused Number
36Testing the New Account Function A Used Number