Transcript

Savitch - Chapters 11&12

CS 150 1

Extending the Class CapabilitiesLet’s 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. //////////////////////////////////////////////////////////////////////////#ifndef BANK_ACCOUNT_Husing 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(string testAccountNumber); bool checkPIN(string testPIN); void deposit(float amount); void withdraw(float amount);

const modifier

Savitch - Chapters 11&12

CS 150 2

BankAcct.h (Continued)

// Accessor Functions bool getActive() const; float getBalance() const; float getInterestRate() const; // Binary Operators bool operator == (const BankAccount &account) const; bool operator < (const BankAccount &account) const; bool operator <= (const BankAccount &account) const; bool operator > (const BankAccount &account) const; bool operator >= (const BankAccount &account) const; bool operator != (const BankAccount &account) const;

// Friend Functions friend void merge(BankAccount &accountA, BankAccount &accountB); friend istream& operator >> (istream &sourceFileStream, BankAccount &account); friend ostream& operator << (ostream &destinationFileStream, const BankAccount &account);

const modifiers

overloaded operators

friend functions

Savitch - Chapters 11&12

CS 150 3

BankAcct.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

We’ll examine the const modifiers, overloaded operators, and friend functions as we come to them in the BankAccount’s

implementation file and in its driver program.

Savitch - Chapters 11&12

CS 150 4

BankAcct.cpp

//////////////////////////////////////////////////////////////////////////// BankAcct.cpp //// //// The class implementation for the NEW AND IMPROVED BankAccount class. ////////////////////////////////////////////////////////////////////////////#include <iomanip>#include <fstream>#include <cstring>#include "BankAcct.h"using namespace std;

// Default constructor. Initializes no data members. //BankAccount::BankAccount(){}

// Initializing constructor. Initializes appropriate data members //// to parameterized values; initializes active data member to true. //BankAccount::BankAccount(string newAccountNumber, string newPIN, float newBalance, float newInterestRate){ setAccountNumber(newAccountNumber); setPIN(newPIN); setBalance(newBalance); setInterestRate(newInterestRate); setActive(true);}

Savitch - Chapters 11&12

CS 150 5

BankAcct.cpp (Continued)// Copy constructor. Duplicates the parameterized BankAccount. //BankAccount::BankAccount(const BankAccount &account){ string acctNbr; string pinNbr;

account.getAccountNumber(acctNbr); setAccountNumber(acctNbr); setActive(account.getActive()); if (active) { account.getPIN(pinNbr); setPIN(pinNbr); setBalance(account.getBalance()); setInterestRate(account.getInterestRate()); } 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.

We’ll see how this is done when we get to theimplementations of those member functions.

Savitch - Chapters 11&12

CS 150 6

BankAcct.cpp (Continued)

// Member function to set the balance data member //// to the parameterized value (if appropriate). //void BankAccount::setBalance(float newBalance){ if (newBalance >= 0.0F) balance = newBalance; return;}

// Member function to set the interestRate data member //// to the parameterized value (if appropriate). //void BankAccount::setInterestRate(float newInterestRate){ if (newInterestRate >= 0.0F) interestRate = newInterestRate; return;}

// Member function to verify whether the accountNumber data //// member has the same value as the parameterized value. //bool BankAccount::checkAccountNumber(string testAccountNumber){ return (accountNumber == testAccountNumber)}

Savitch - Chapters 11&12

CS 150 7

BankAcct.cpp (Continued)

// Member function to verify whether the pin data member //// has the same value as the parameterized value. //bool BankAccount::checkPIN(string testPIN){ return (pin == testPIN)}

// Member function to increment the balance data //// member by the parameterized value (if appropriate). //void BankAccount::deposit(float amount){ if (amount > 0.0F) balance += amount;}

// Member function to decrement the balance data //// member by the parameterized value (if appropriate). //void BankAccount::withdraw(float amount){ if (amount <= balance) balance -= amount; return;}

Savitch - Chapters 11&12

CS 150 8

BankAcct.cpp (Continued)// Accessor member function for the active data member. //bool BankAccount::getActive() const{ return active;}

// Accessor member function for the balance data member. //float BankAccount::getBalance() const{ return balance;}

// Accessor member function for the interestRate data member. //float BankAccount::getInterestRate() const{ return interestRate;}

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.

const modifiers

Savitch - Chapters 11&12

CS 150 9

BankAcct.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 BankAccount::operator == (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));}

Several C++ operators (==, <, <=, >, >=, !=, =, +, -, *, /, %, ++, --, +=, -=, [], 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.

overloaded operator

Savitch - Chapters 11&12

CS 150 10

BankAcct.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 BankAccount::operator < (const BankAccount &account) const{ if (!account.active) return false; else if (!active) return true; else if (interestRate < account.interestRate) return true; else if (interestRate > account.interestRate) return false; else return (balance < account.balance);}

// Less-than-or-equal operator. An account is considered //// less than or equal to another account if it is either //// less than (<) or equal to (==) the other account. //bool BankAccount::operator <= (const BankAccount &account) const{ return ((*this < account) || (*this == account));}

Notice that the <= operator has been defined to take advantage

of the previously

defined < and == operators.

Also, observe the use of *this when referring to the “calling object”, in this case, the first BankAccount in

the comparison.

overloaded operators

Savitch - Chapters 11&12

CS 150 11

BankAcct.cpp (Continued)

// Greater-than operator. An account is considered greater than another //// account if the second account is less than (<) the first account. //bool BankAccount::operator > (const BankAccount &account) const{ return (account < *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 (<=) the first account. //bool BankAccount::operator >= (const BankAccount &account) const{ return ((*this > account) || (*this == account));}

// Inequality operator. An account is considered unequal to another //// account if the account is not equal to (==) the other account. //bool BankAccount::operator != (const BankAccount &account) const{ return !(*this == account);}

Again, each of these operators has been defined to take advantage of the previously defined operators.

Also, observe that it’s okay to reverse the roles of *this and the parameter account in the > operator’s definition, since both BankAccount values have been subjected to a

const modifier.

overloaded operators

Savitch - Chapters 11&12

CS 150 12

BankAcct.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 >> (istream &sourceFileStream, BankAccount &account){ string accountNbr; char activeIndicator; string pinNbr; float bal; float intRate;

sourceFileStream >> accountNbr; account.setAccountNumber(accountNbr); sourceFileStream >> activeIndicator; if (activeIndicator != 'Y') account.setActive(false); else { account.setActive(true); sourceFileStream >> pinNbr; account.setPIN(pinNbr); sourceFileStream >> bal; account.setBalance(bal); sourceFileStream >> intRate; account.setInterestRate(intRate); } return sourceFileStream;}

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.

friend function

Savitch - Chapters 11&12

CS 150 13

BankAcct.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 << (ostream &destinationFileStream, const BankAccount &account){ destinationFileStream.setf(ios::fixed); destinationFileStream << setprecision(2); string accountNbr; account.getAccountNumber(accountNbr); destinationFileStream << "Account Number: " << accountNbr << endl; if (account.getActive() == false) destinationFileStream << "**** ACCOUNT INACTIVE ****"; else { string pinNbr; account.getPIN(pinNbr); destinationFileStream << "PIN: " << pinNbr << endl; destinationFileStream << "Interest Rate: "

<< 100*account.getInterestRate() << '%' << endl; destinationFileStream << "Current Balance: $" << account.getBalance(); } return destinationFileStream;}

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.

friend function

Savitch - Chapters 11&12

CS 150 14

BankAcct.cpp (Continued)

// Member function to set the active data member to the parameterized value. //void BankAccount::setActive(bool newActive){ active = newActive; return;}

// Member function to set the accountNumber data member to the parameterized value. //void BankAccount::setAccountNumber(string newAccountNumber){ accountNumber = newAccountNumber; return;}

// Member function to set the pin data member to the parameterized value. //void BankAccount::setPIN(string newPIN){ pin = newPIN; return;}

Savitch - Chapters 11&12

CS 150 15

BankAcct.cpp (Continued)

// Accessor member function for the accountNumber data member. //string BankAccount::getAccountNumber() const{

return accountNumber;}

// Accessor member function for the pin data member. //void BankAccount::getPIN() const{

return pin;}

Finally, the last two accessor functions for the BankAccount class have const modifiers to assure the compiler that they don’t alter the “calling object”,

the BankAccount *this.

const modifiers

To complete our introduction to these new features, we’ll next examine a driver program to test the BankAccount class.

Savitch - Chapters 11&12

CS 150 16

BankManagerDriver.cpp

//////////////////////////////////////////////////////// BankManagerDriver.cpp //// //// This program permits a bank manager to retrieve, //// peruse, and alter a list of BankAccount data. ////////////////////////////////////////////////////////#include <iostream>#include <fstream>#include <cstdlib>#include <string>#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 driver’s 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.

Savitch - Chapters 11&12

CS 150 17

BankManagerDriver.cpp (Continued)

//////////////////////////////////////////////////////////////////////// The main function drives the bank manager's interactive session. ////////////////////////////////////////////////////////////////////////void main(){ BankAccount acctList[MAX_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;}

Savitch - Chapters 11&12

CS 150 18

BankManagerDriver.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.accountNumber == accountB.accountNumber) return; if ((accountA.active) && (accountB.active)) { if (accountA >= accountB) { accountA.deposit(accountB.balance); accountB.setActive(false); } else { accountB.deposit(accountA.balance); accountA.setActive(false); } } return;}

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!

friend function

Savitch - Chapters 11&12

CS 150 19

BankManagerDriver.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 << "Enter the name of the file containing the bank account data: "; cin >> bankFileName; bankFile.open(bankFileName.c_str());

bankFile >> length; for (int i = 0; i < length; i++) bankFile >> list[i];

bankFile.close();}

Savitch - Chapters 11&12

CS 150 20

BankManagerDriver.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 << "Select the letter corresponding to the function you wish to perform:” << endl << endl; cout << '\t' << "U - Update an existing account" << endl << '\t' << "V - View an existing account" << endl << '\t' << "N - Open a new account" << endl << '\t' << "M - Merge two existing accounts" << endl << '\t' << "Q - Quit processing bank accounts" << endl << endl << "SELECT ONE LETTER NOW: "; cin >> ltr; ltr = toupper(ltr); while ((ltr != 'U') && (ltr != 'V') && (ltr != 'N') && (ltr != 'M') && (ltr != 'Q')) { cout << "You must choose one of the letters listed above. Try again: "; cin >> ltr; ltr = toupper(ltr); } return ltr;}

Savitch - Chapters 11&12

CS 150 21

BankManagerDriver.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 index[2];

for (int i = 0; i <= 1; i++) { if (i == 0) cout << "Enter the account number of the first account: "; else cout << "Enter the account number of the second account: "; cin >> acctNbr; index[i] = indexOfAccount(acctNbr, list, length); if (index[i] < 0) { cout << "NO ACCOUNT WITH THAT NUMBER. REQUEST DENIED." << endl << endl; return; } } merge(list[index[0]], list[index[1]]); cout << "ACCOUNT MERGER PROCESSED." << endl << endl; return;}

Savitch - Chapters 11&12

CS 150 22

BankManagerDriver.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 << "NO ROOM FOR MORE ACCOUNTS. REQUEST DENIED." << endl << endl; return; } cout << "Enter the account number for the new account: "; cin >> newAcctNbr; index = indexOfAccount(newAcctNbr, list, length); if (index >= 0) { cout << "DUPLICATE ACCOUNT NUMBER. REQUEST DENIED." << endl << endl; return; }

Savitch - Chapters 11&12

CS 150 23

BankManagerDriver.cpp (Continued)

cout << "Enter the PIN for the new account: ";cin >> newPIN;cout << "Enter the initial balance for the new account: $";cin >> newBal;cout << "Enter the interest rate for the new account: ";cin >> newIntRate;BankAccount newAcct(newAcctNbr, newPIN, newBal, newIntRate);list[length] = newAcct;length++;cout << "NEW ACCOUNT PROCESSED." << endl << endl;return;

}

Savitch - Chapters 11&12

CS 150 24

BankManagerDriver.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. ////////////////////////////////////////////////////////////////////////////void updateAccount(BankAccount list[], int length){ string acctNbr; int index; char request; float amt;

cout << "Enter the account number of the account being updated: "; cin >> acctNbr; index = indexOfAccount(acctNbr, list, length); if (index < 0) { cout << "NO ACCOUNT WITH THAT NUMBER. REQUEST DENIED." << endl << endl; return; }

Savitch - Chapters 11&12

CS 150 25

BankManagerDriver.cpp (Continued)

cout << "Enter \"D\" for deposit or \"W\" for withdrawal: "; cin >> request; request = toupper(request); while ((request != 'D') && (request != 'W')) { cout << "You must enter \"D\" or \"W\". Please try again: "; cin >> request; request = toupper(request); }

cout << "Enter the amount: $"; cin >> amt; if (request == 'D') list[index].deposit(amt); else list[index].withdraw(amt); cout << "ACCOUNT UPDATE PROCESSED." << endl << endl; return;}

Savitch - Chapters 11&12

CS 150 26

BankManagerDriver.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 << "Enter the account number of the account to be displayed: "; cin >> acctNbr; index = indexOfAccount(acctNbr, list, length); if (index < 0) { cout << "NO ACCOUNT WITH THAT NUMBER. REQUEST DENIED." << endl << endl; return; } cout << list[index] << endl << endl; return;}

Savitch - Chapters 11&12

CS 150 27

BankManagerDriver.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 < length; i++) { if (list[i].checkAccountNumber(acctNbr)) return i; } return -1;}

Now let’s test the BankAccount class and this new driver.

Savitch - Chapters 11&12

CS 150 28

Testing the View Function: Active & Inactive

Savitch - Chapters 11&12

CS 150 29

Testing the View Function: Bad & Good Account Numbers

Savitch - Chapters 11&12

CS 150 30

Testing the Merge Function

Savitch - Chapters 11&12

CS 150 31

Testing the Update Function: Withdrawing Funds

Savitch - Chapters 11&12

CS 150 32

Testing the Update Function: Withdrawing Too Much

Savitch - Chapters 11&12

CS 150 33

Testing the Update Function: Depositing

Savitch - Chapters 11&12

CS 150 34

Testing the Update Function: Depositing a Negative

Savitch - Chapters 11&12

CS 150 35

Testing the New Account Function: An Unused Number

Savitch - Chapters 11&12

CS 150 36

Testing the New Account Function: A Used Number


Top Related