680 likes | 822 Views
Chapter 8: Advanced C++ Topics. INHERITANCE. VIRTUAL FUNCTIONS. FRIENDS. CLASS TEMPLATES. OVERLOADED OPERATORS. FUNCTION TEMPLATES. CS 240. 53. Membership Categories. When a class is defined, its members (both data members and member functions) must be categorized. Public Members
E N D
Chapter 8: Advanced C++ Topics INHERITANCE VIRTUAL FUNCTIONS FRIENDS CLASS TEMPLATES OVERLOADED OPERATORS FUNCTION TEMPLATES CS 240 53
Membership Categories When a class is defined, its members (both data members and member functions) must be categorized. Public Members Accessible to all driver programs and other classes that utilize the class. Protected Members Accessible only to member functions (and friends) of the class and its derived classes. Private Members Accessible only to member functions (and friends) of the class itself. CS 240 54
class A A’s public members A’s protected members A’s private members class X: public A class Y: protected A class Z: private A X’s public members Y’s public members Z’s public members A’s public members Y’s protected members Z’s protected members X’s protected members A’s public members A’s protected members Z’s private members A’s protected members A’s public members X’s private members Y’s private members A’s protected members Inheritance When a new class is merely an extension of an existing class, the class may be defined as a “derived” class, which “inherits” from the existing class. CS 240 55
Inheritance Example #1: Bank Accounts //////////////////////////////////////////////////////////////////////////// // Class definition file: Account.h // // In this header file, a class is defined to hold information about a // // bank Account. Notice that the only real piece of data inside an object // // of this class is a single floating-point number that represents the // // Account's current balance. Again, the idea of forming a class like // // this is to hide the information so that it can only be accessed // // through precisely specified means, to centralize those means so that // // modifying them at a later date can be simplified, and to make it easy // // to formulate slight variations of the Account class (e.g., a checking // // Account that accumulates interest, a savings Account, etc.). // //////////////////////////////////////////////////////////////////////////// #ifndefACCT_H class Account { public: // Class constructors Account(); Account(floatinitialBalance); Account(const Account &a); // Member functions float deposit(float amount); float withdraw(float amount); float getBalance(); Account& operator = (const Account &a); protected: // Data member float balance; }; #define ACCT_H #endif CS 240 56
// Class implementation file: Account.cpp // // The implementations of the member functions of the // // Account class are contained in this program file. // #include "Account.h" // Default Constructor: Handles the creation of an // // Account with an unspecified initial balance. // Account::Account() { balance = 0.00F; } // Initializing Constructor: Handles the creation // // of an Account with a specified initial balance. // Account::Account(floatinitialBalance) { balance = initialBalance; } // Copy Constructor: Handles the creation of an Account // // that is a duplicate of the parameterized Account. // Account::Account(const Account &a) { balance = a.balance; } // Deposit Member Function: Adds a deposited // // amount to the balance of an Account. // float Account::deposit(float amount) { balance = balance + amount; return balance; } CS 240 57
// Withdraw Member Function: Subtracts a withdrawal amount from the // // balance of an Account, returning the modified balance if there are // // sufficient funds and a negative value if insufficient funds exist. // float Account::withdraw(float amount) { if (amount > balance) return -1.0F; else { balance = balance - amount; return balance; } } // GetBalance Member Function: Retrieves the Account balance. // // This is the only way for a function that is not a member // // function of the Account class (or one of its derived // // classes) to access the current Account balance. // float Account::getBalance() { return balance; } // Assignment Operator: Overloads the assignment operator, so a // // variable of type Account can be used in an assignment statement. // Account& Account::operator = (const Account &a) { balance = a.balance; return *this; } CS 240 58
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Class definition file: CreditAccount.h // // In this header file, a subclass of the Account class is defined // // to hold information about a CreditAccount. All of the members // // of the Account class are members of the CreditAccount subclass, // // and the subclass has three additional members: an initializing // // constructor, one data member to hold the CreditAccount's daily // // rate for finance charges, and one public member function to // // apply the finance charges to the balance. // ///////////////////////////////////////////////////////////////////// #ifndefCRED_ACCT_H #include "Account.h“ class CreditAccount: public Account { public: // Class constructor CreditAccount(floatinitialBalance, float finChgRate); // Member function float withdraw(float amount); void applyFinanceCharge(); protected: // Data member float dailyFinChgRate; }; #define CRED_ACCT_H #endif CS 240 59
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Class implementation file: CreditAccount.cpp // // This program file contains the implementations of the member // // functions of the CreditAccount subclass of the account class. // /////////////////////////////////////////////////////////////////// #include "CreditAccount.h" // The initializing constructor for the CreditAccount class invokes the initializing // // constructor for the Account class, which initializes the balance data member. It // // then gives an initial value to the data member representing the finance charge rate. // CreditAccount::CreditAccount(floatinitialBalance, float finChgRate): Account(initialBalance) { dailyFinChgRate = finChgRate; } // The withdraw member function for the CreditAccount subclass supercedes the withdraw // // member function for the Account superclass; it subtracts a withdrawal amount from the // // balance of a CreditAccount, returning the resulting balance (even if it's negative). // floatCreditAccount::withdraw(float amount) { balance -= amount; return balance; } // The applyFinanceCharge member function updates a CreditAccount's balance by taking // // the product of the CreditAccount's finance charge rate and the amount currently // // owed on the CreditAccount, and subtracting that product from the balance. // void CreditAccount::applyFinanceCharge() { if (getBalance() < 0.0F) withdraw(-1.0F * getBalance() * dailyFinChgRate); } CS 240 60
class Account PUBLIC: deposit withdraw getBalance = operator PROTECTED: balance class CreditAccount: public Account PUBLIC: withdraw applyFinanceCharge PROTECTED: dailyFinChgRate deposit withdraw getBalance = operator balance The CreditAccountDerived Class & Its Inheritance from the Account Class Note that CreditAccount has access to both withdraw functions! CS 240 61
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Class definition file: DebitAccount.h // // In this header file, a subclass of the Account class is defined to hold // // information about a DebitAccount. All of the members of the Account // // class are members of the DebitAccount subclass, and the subclass has 3 // // additional members: an initializing constructor, one data member to // // hold the DebitAccount's daily interest rate, and one public member // // function to apply this interest rate to the current balance. // ///////////////////////////////////////////////////////////////////////////// #ifndefDEB_ACCT_H #include "Account.h" class DebitAccount: public Account { public: // Class constructor DebitAccount(floatinitialBalance, float intRate); //Member function void compoundInterest(); protected: // Data member float dailyIntRate; }; #define DEB_ACCT_H #endif CS 240 62
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Class implementation file: DebitAccount.cpp // // This program file contains the implementations of the member // // functions of the DebitAccount subclass of the Account class. // ////////////////////////////////////////////////////////////////// #include "DebitAccount.h" // The initializing constructor for the DebitAccount class invokes the initializing // // constructor for the Account class, which initializes the balance data member. // // It then gives an initial value to the interest data member. // DebitAccount::DebitAccount(floatinitialBalance, float intRate): Account(initialBalance) { dailyIntRate = intRate; } // The compoundInterest function updates a DebitAccount's balance // // by taking the product of the incoming interest rate and the // // DebitAccount's balance, and adding that product to the balance. // void DebitAccount::compoundInterest() { if (getBalance() > 0.0F) deposit(getBalance() * dailyIntRate); } CS 240 63
class Account PUBLIC: deposit withdraw getBalance = operator PROTECTED: balance class DebitAccount: public Account PUBLIC: compoundInterest PROTECTED: dailyIntRate deposit withdraw getBalance = operator balance The DebitAccountDerived Class & Its Inheritance from the Account Class It’s possible to create a new class that inherits from both the CreditAccount class and the DebitAccount class, but the fact that both inherit from the Account class makes such a move somewhat problematic... CS 240 64
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Class definition file: DebitCreditAccount.h // // In this header file, a subclass of the CreditAccount class is defined to hold // // information about an account that is essentially both a DebitAccount and a // // CreditAccount. Inheriting members from the CreditAccount class, it only needs 3 // // additional members: an initializing constructor, a function to compound the daily // // interest on the account, and a data member representing a daily interest rate. // /////////////////////////////////////////////////////////////////////////////////////// #ifndefDEB_CRED_ACCT_H #include "CreditAccount.h" class DebitCreditAccount : public CreditAccount { public: // Class constructor DebitCreditAccount(floatinitialBalance, float intRate, float finChgRate); //Member function void compoundInterest(); protected: // Data member float dailyIntRate; }; #define DEB_CRED_ACCT_H #endif CS 240 65
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Class implementation file: DebitCreditAccount.cpp // // This program file contains the implementation of the member function // // of the DebitCreditAccount subclass whichinherits from the CreditAccount // // class and has additional characteristics of the DebitAccount class. // ///////////////////////////////////////////////////////////////////////////// #include "DebitCreditAccount.h" // The DebitCreditAccount initializing constructor inherits from // // the CreditAccount initializing constructor, with the addition // // of an initializing assignment of the dailyIntRate data member. // DebitCreditAccount::DebitCreditAccount (floatinitialBalance, float intRate, float finChgRate): CreditAccount(initialBalance, finChgRate) { dailyIntRate = intRate; } // The compoundInterest function updates a DebitCreditAccount's balance // // by taking the product of the incoming interest rate & the // // DebitCreditAccount's balance, and adding that product to the balance. // void DebitCreditAccount::compoundInterest() { if (getBalance() > 0.0F) deposit(getBalance() * dailyIntRate); } CS 240 66
The DebitCreditAccount Derived Class & Its Inheritance from the CreditAccount & Account Classes class Account PUBLIC: deposit withdraw getBalance = operator class CreditAccount: public Account class DebitCreditAccount: public CreditAccount PROTECTED: balance PUBLIC: withdraw applyFinanceCharge PUBLIC: compoundInterest withdraw applyFinanceCharge deposit withdraw getBalance = operator deposit withdraw getBalance = operator PROTECTED: dailyIntRate PROTECTED: dailyFinChgRate dailyFinChgRate balance balance CS 240 67
// Program file: DebitCreditDriver.cpp // // This program tests various aspects of the Account class and its three // // subclasses: DebitAccount, CreditAccount, and DebitCreditAccount. // #include <iostream> #include <iomanip> #include <fstream> #include "DebitAccount.h" #include "CreditAccount.h" #include "DebitCreditAccount.h" using namespace std; void printBalance(constchar name[], Account &a); // The main function sets up three variables, one of type DebitAccount, one of type // // CreditAccount, and one of type DebitCreditAccount. It then cycles through a month's// // banking transactions for each Account, comparing what the results would be if a // // person used two Accounts (an interest-generating DebitAccount in which funds are // // deposited and a finance-charge-generating CreditAccount from which money is removed // // when needed) versus if a person used a single DebitCreditAccount, which generates // // interest on those days when it has a positive balance and incurs a finance charge // // otherwise. Output messages indicating the initial and the final status of each // // Account are generated for a specific sample month. // void main() { DebitAccount deb(1000.00F, 0.0001F); CreditAccount cred(0.00F, 0.0005F); DebitCreditAccountdeb_cred(1000.00F, 0.0001F, 0.0005F); // The next variable is a floating-point value that represents a transaction for the // Accounts in question, with positive numbers used for deposits and negative numbers // used for withdrawals. It is assumed that a person using the two-Account approach // always applies three-quarters of any deposited amount to the CreditAccount, and // the other quarter to the DebitAccount. It is also assumed that this person retrieves // half of each withdrawn amount from the CreditAccount, and half from the DebitAccount. float transaction; CS 240 68
ifstreaminputFile; intnbrOfTransactions; cout.setf(ios::fixed); cout << setprecision(2); cout << "INITIAL STATUS OF ACCOUNTS\n"; printBalance("Debit: ", deb); printBalance("Credit: ", cred); printBalance("Debit-Credit: ", deb_cred); // This section of code loops through the daily transactions for the three Accounts. inputFile.open("transactions.txt"); nbrOfTransactions = 0; inputFile >> transaction; while (!inputFile.eof()) { nbrOfTransactions++; if (transaction < 0.0F) { deb.withdraw(-0.5F * transaction); cred.withdraw(-0.5F * transaction); deb_cred.withdraw(-transaction); } else if (transaction > 0.0F) { deb.deposit(0.25F * transaction); cred.deposit(0.75F * transaction); deb_cred.deposit(transaction); } deb.compoundInterest(); cred.applyFinanceCharge(); deb_cred.compoundInterest(); deb_cred.applyFinanceCharge(); inputFile >> transaction; } CS 240 69
// This section of code loops through the daily transactions for the three Accounts. inputFile.open("transactions.txt"); nbrOfTransactions = 0; inputFile >> transaction; while (!inputFile.eof()) { nbrOfTransactions++; if (transaction < 0.0F) { deb.withdraw(-0.5F * transaction); cred.withdraw(-0.5F * transaction); deb_cred.withdraw(-transaction); } else if (transaction > 0.0F) { deb.deposit(0.25F * transaction); cred.deposit(0.75F * transaction); deb_cred.deposit(transaction); } deb.compoundInterest(); cred.applyFinanceCharge(); deb_cred.compoundInterest(); deb_cred.applyFinanceCharge(); inputFile >> transaction; } cout << endl << "FINAL STATUS OF ACCOUNTS\n"; printBalance("Debit: ", deb); printBalance("Credit: ", cred); printBalance("Debit-Credit: ", deb_cred); cout << endl; return; } CS 240 70
// Function printBalance outputs the current // ‘’ balance of the Account. Note that it can // // use an Account as its second parameter. // void printBalance(constchar header[], Account &a) { cout << header << '$' << a.getBalance() << endl; } 200.00 0.00 0.00 -100.00 0.00 -160.00 -40.00 -600.00 0.00 0.00 -80.00 0.00 0.00 0.00 -600.00 0.00 400.00 0.00 0.00 0.00 200.00 -800.00 0.00 0.00 200.00 0.00 0.00 0.00 -120.00 500.00 Input File: transactions.txt Resulting Output CS 240 71
// Class declaration file: queue.h // Array implementation of the queue ADT. #ifndefQUEUE_H #include <fstream> using namespace std; typedefintelementType; constint MAX_QUEUE_SIZE = 10; class Queue { public: // Class constructors Queue(); Queue(const Queue &q); // Member functions boolisEmpty() const; void enqueue(constelementType &item); elementTypedequeue(); voidinputEnqueue(ifstream &input); friendostream& operator << (ostream &destFile, const Queue &q); protected: // Data members intfront, rear, length; elementType list[MAX_QUEUE_SIZE]; // Member function boolisFull() const; }; #define QUEUE_H #endif Inheritance Example #2: Weeding Queues • New Queue Functionality: • Enqueue an entire input file • Queue output operator CS 240 72
// Class implementation file: Queue.cpp // Array implementation of the Queue ADT. #include "Queue.h" #include <assert.h> // Default constructor: Make empty Queue // Queue:: Queue() { front = 0; rear = MAX_QUEUE_SIZE - 1; length = 0; } // Copy constructor: Make copy of Queue. // Queue:: Queue(const Queue &q) { intindex; front = q.front; rear = q.rear; length = q.length; for (int i = 0; i < length; i++) { index = (i + q.front) % MAX_QUEUE_SIZE; list[index] = q.list[index]; } } // isEmpty function: signals if // // *this is an empty Queue. // boolQueue:: isEmpty() const { return (length == 0); } // Enqueue function; inserts a new // // item into the rear of Queue *this // // (if there's enough room). // void Queue:: enqueue(constelementType &item) { assert(!isFull()); rear = (rear + 1) % MAX_QUEUE_SIZE; list[rear] = item; length++; } // Dequeue function; remove the item // // at the front of Queue *this (if // // there’s one there). // elementType Queue:: dequeue() { elementType item; assert(!isEmpty()); item = list[front]; front = (front + 1) % MAX_QUEUE_SIZE; length--; return item; } // isFull function; returns bool// // indicating if *this is a full // // Queue (wrt the array). // boolQueue:: isFull() const { return (length == MAX_QUEUE_SIZE); } CS 240 73
// InputEnqueue function: inputs items in // // param. file, enqueueing each into *this. // void Queue::inputEnqueue(ifstream &input) { elementType item; input >> item; while (!input.eof()) { enqueue(item); input >> item; } return; } // Output operator: outputs Queue elts to parameterized output // // file, from front of Queue to rear, w/o removing items. // ostream& operator << (ostream &destFile, constQueue &q) { intindex; for (int i = 0; i < q.length; i++) { index = (i + q.front) % MAX_QUEUE_SIZE; destFile << q.list[index] << endl; } return destFile; } CS 240 74
// Class declaration file: WeedingQueue.h // This subclass of the Queue class; //"weeds "out smaller values whenever //the structureis about to overflow. #ifndefWEEDING_QUEUE_H #include "Queue.h" class WeedingQueue: public Queue { public: // Member function void enqueue(constelementType &item); }; #define WEEDING_QUEUE_H #endif void WeedingQueue:: enqueue(constelementType &item) { intindex; if (isFull()) { // Locate the smallest element. intminIndex = 0; for (index = 1; index < length; index++) if (list[(front+index) % MAX_QUEUE_SIZE] < list[(front+minIndex) % MAX_QUEUE_SIZE]) minIndex = index; // If the new item is smaller, don't insert it. if (item < list[(front+minIndex)%MAX_QUEUE_SIZE]) return; // Otherwise, remove the smallest element. for (index = minIndex; index < length; index++) list[(front + index) % MAX_QUEUE_SIZE] = list[(front + index + 1) % MAX_QUEUE_SIZE]; length--; rear = (front + length - 1) % MAX_QUEUE_SIZE; } // Now insert the new element. rear = (rear+1) % MAX_QUEUE_SIZE; list[rear] = item; length++; } // Class implementation file: // WeedingQueue.cpp // This subclass of the Queue class; //"weeds "out smaller values whenever //the structure is about to overflow. #include "WeedingQueue.h" // Enqueue function; inserts a new item // in the rear of WeedingQueue *this //(if there is enough room). If there //isn’t enough room, the smallest //element in the WeedingQueue is //removed (even if it's not at the //front of the WeedingQueue), and then //the new item is inserted at the rear // of the WeedingQueue. CS 240 75
Contents of queueData.txt: // Program file: WeedingQueueDriver.cpp // // This program tests the WeedingQueue // // class by inserting 50 values via the // // inputEnqueue member function. // #include <iostream> #include <fstream> #include"WeedingQueue.h" using namespace std; // The main function inputs and // // outputs a WeedingQueue. // void main() { WeedingQueuewq; ifstreaminputFile; inputFile.open("queueData.txt"); wq.inputEnqueue(inputFile); cout << wq; return; } 33 56 82 90 47 10 94 36 89 21 88 35 91 17 53 28 59 82 36 72 16 38 78 92 73 16 83 95 19 66 46 20 39 65 48 27 63 49 14 84 62 31 93 38 77 80 11 98 67 45 Resulting Output: Why did this fail? CS 240 76
Virtual Functions & Late Binding The problem in the previous example relates to the fact that the WeedingQueue subclass used the inputEnqueue function that it inherited from the Queue class. void Queue::inputEnqueue(ifstream &input) { elementType item; input >> item; while (!input.eof()) { enqueue(item); input >> item; } return; } When the compiler analyzes this code, it’s doing it in the context of the Queue class. Consequently, it “binds” this line to the enqueue member function of the Queue class (which doesn’t handle overflow), not the enqueue member function of the WeedingQueue subclass (which does handle overflow)! CS 240 77
// Class declaration file: queue.h // Array implementation of the queue ADT. #ifndefQUEUE_H #include <fstream> using namespacestd; typedefintelementType; constMAX_QUEUE_SIZE = 10; class Queue { public: // Class constructors Queue(); Queue(const Queue &q); // Member functions boolisEmpty() const; virtual void enqueue(const elementType &item); elementTypedequeue(); void inputEnqueue(ifstream &input); friend ostream& operator << (ostream &destFile, constQueue &q); protected: // Data members intfront, rear, length; elementType list[MAX_QUEUE_SIZE]; // Member function boolisFull() const; }; #define QUEUE_H #endif // Class declaration file: WeedingQueue.h // This subclass of the Queue class; // "weeds" out smaller values whenever // the structure is about to overflow. #ifndefWEEDING_QUEUE_H #include "Queue.h" class WeedingQueue: public Queue { public: // Member function virtual void enqueue(const elementType &item); }; #define WEEDING_QUEUE_H #endif To ensure that the correct context is used, declare each version of enqueue as a “virtual” function. Then binding won’t occur until execution time! CS 240 78
Q = Instance of Queue Generally, follow these guidelines when deciding whether to make a member function virtual: VMT for Q Default Constructor Copy Constructor isEmpty Function enqueue Function • Make the functions of the base class virtual, so they can be overridden. inputEnqueue Function Input Operator • C++ doesn’t support virtual constructors. (When an object is declared, a Virtual Method Table is created, with pointers to all of its virtual functions. Since the VMT is created as part of the constructor process, the constructor itself cannot be one of the virtual methods.) WQ = Instance of WeedingQueue VMT for WQ Default Constructor Code for Default Constructor Copy Constructor isEmpty Function Code for Copy Constructor enqueue Function Code for isEmpty Function inputEnqueue Function Input Operator Code for enqueue Function • Always make destructors virtual. (This ensures that the derived class destructor is called before the base class destructor, avoiding memory leaks.) Code for enqueue Function Code for inputEnqueue Function Code for Input Operator CS 240 79
Friends There are occasions when it is necessary to give a class or function access to the private members of another class. On those occasions, the class or function that needs that access may be declared a friend of the class that’s giving access. • Examples: • I/O operators • Binary operators • Dependent classes CS 240 80
Example: Affine Transformations // Class Definition File: Matrix.h // // The Mtrxclass uses homogeneous // // coordinates to represent 2D affine // // transformations, while the Vctr // // class uses homogeneous coordinates // // to represent 2D points. // #ifndefMATRIX_H #include <fstream> using namespace std; classMtrx; classVctr { public: // Constructors Vctr(); Vctr(constVctr &v); Vctr(constfloat &x, const float &y); private: // Data Members floatcoord[3]; // Friends friendostream& operator << (ostream &os, constVctr &v); friendVctroperator * (constMtrx &m, constVctr &v); friend class Mtrx; }; classMtrx { public: // Constructors Mtrx(); Mtrx(constMtrx &m); Mtrx(constint&angle); Mtrx(constfloat &scaleFactor); Mtrx(constfloat &translateX, constfloat &translateY); private: // Data Members Vctrcolumn[3]; // Friends friendVctroperator * (constMtrx &m, constVctr &v); }; #define MATRIX_H #endif Gives the entire Matrix class access to the coord data member and all of the constructors Gives the multiplication operator access to the coord data member and all of the constructors Gives the multiplication operator access to the column data member and all of the Matrix constructors Gives the output operator access to the coord data member and all of the constructors CS 240 81
// Class Implementation File: Mtrx.cpp #include <math.h> #include "Matrix.h" const float PI = 3.14159F; // Vctr default constructor: Sets up null Vctr (weight factor 1). Vctr::Vctr() { coord[0] = coord[1] = 0; coord[2] = 1; } // Vctrcopy constructor: Duplicates the parameterized Vctr. Vctr::Vctr(constVctr &v) { inti; for(i = 0; i <= 2; i++) coord[i] = v.coord[i]; } // Vctr initializing constructor: Sets up a weight-1 // Vctr with the parameterized x and y coordinates. Vctr::Vctr(constfloat &x, const float &y) { coord[0] = x; coord[1] = y; coord[2] = 1; } // Vctr output operator ostream& operator << (ostream &os, const Vctr &v) { if(v.coord[2] == 0) os<< "(" << 0.0 << "," << 0.0 << ")"; else os<< "(" << v.coord[0]/v.coord[2] << ","<< v.coord[1]/v.coord[2] << ")"; returnos; } CS 240 82
// Mtrx default constructor: Sets up identity Mtrx. Mtrx::Mtrx() { inti,j; for(i = 0; i <= 2; i++) for(j = 0; j <= 2; j++) column[i].coord[j] = (i == j) ? 1.0F : 0.0F; return; } // Mtrx copy constructor: Duplicates each Mtrx slot. Mtrx::Mtrx(constMtrx &m) { inti, j; for(i = 0; i <= 2; i++) for(j = 0; j <= 2; j++) column[i].coord[j] = m.column[i].coord[j]; return; } // Mtrx initializing constructor: sets up Mtrxfor rotating the parameterized # of degrees. Mtrx::Mtrx(constint&angle) { floatfloatAngle = PI * angle / 180.0F; column[0].coord[0] = column[1].coord[1] = cos(floatAngle); column[0].coord[1] = sin(floatAngle); column[1].coord[0] = -sin(floatAngle); column[0].coord[2] = column[2].coord[0] = column[1].coord[2] = column[2].coord[1] = 0.0F; column[2].coord[2] = 1.0F; return; } CS 240 83
// Mtrx initializing constructor: sets up Mtrx for scaling by the parameterized factor. Mtrx::Mtrx(constfloat &scaleFactor) { column[0].coord[0] = column[1].coord[1] = scaleFactor; column[2].coord[2] = 1.0F; column[0].coord[1] = column[0].coord[2] = column[1].coord[0] = column[1].coord[2] = column[2].coord[0] = column[2].coord[1] = 0.0; return; } // Mtrx initializing constructor: sets up Mtrxfor translating in the parameterized direction. Mtrx::Mtrx(constfloat &translateX, const float &translateY) { column[0].coord[0] = column[1].coord[1] = column[2].coord[2] = 1.0F; column[0].coord[1] = column[1].coord[0] = column[0].coord[2] = column[1].coord[2] = 0.0F; column[2].coord[0] = translateX; column[2].coord[1] = translateY; return; } // Multiplication operator to apply a transformation // Mtrx to a Vctr and obtain the transformed Vctr. Vctroperator * (constMtrx &m, constVctr &v) { Vctrresult; inti, j; for(i = 0; i <= 2; i++) { result.coord[i] = 0.0F; for(j = 0; j <= 2; j++) result.coord[i] += m.column[j].coord[i] * v.coord[j]; } returnresult; } CS 240 84
// Driver file: MatrixDriver.cpp // This program queries the user to specify two 2D affine // transformations (scaling, translation, rotation) and a // pair of 2D coordinates. It then compares what happens // when the transformations are applied to the coordinates // in either order (specifically, whether the same result // is produced). #include <iostream> #include <string> #include "Matrix.h" using namespace std; MtrxretrieveTransformation(string &label); VctrretrieveVector(); void main() { string headerA = "Transformation A: "; string headerB = "Transformation B: "; MtrxmatA = retrieveTransformation(headerA); MtrxmatB = retrieveTransformation(headerB); Vctrv = retrieveVector(); Vctr ABv = matA * (matB * v); VctrBAv = matB * (matA * v); cout<< endl << headerA << endl << headerB << endl << endl; cout.setf(ios::fixed); cout.setf(ios::showpoint); cout.precision(2); cout<< "Transform by A, then by B: " << BAv << endl; cout<< "Transform by B, then by A: " << ABv << endl; cout<< endl << "Enter anything to exit: "; cin>> headerA; } CS 240 85
// The retrieve Transformation function queries the // user regarding a transformation's type and parameters, // creates the corresponding transformation matrix, // alters the parameterized label to indicate the // type of transformation, and then returns the matrix. MtrxretrieveTransformation(string &label) { chartransformType; cout<< "Specify which type of affine transformation" << endl << "(R: Rotation, S: Scaling, T: Translation)" << endl << "you want to use for " << label; cin>> transformType; transformType= toupper(transformType); while(string("RST").find(transformType) < 0) { cout<< "Try again (RST): "; cin>> transformType; transformType= toupper(transformType); } cout<< endl; switch(transformType) { case'R': { intnbrDegrees; cout<< "Enter the number of degrees for the rotation: "; cin>> nbrDegrees; cout<< endl; label = label + "Rotation"; MtrxresultMtrx(nbrDegrees); returnresultMtrx; } CS 240 86
case'S': { floatscale; cout<< "Enter the scaling factor: "; cin>> scale; cout<< endl; label = label + "Scaling"; MtrxresultMtrx(scale); returnresultMtrx; } default: { floatx, y; cout<< "Enter the translation amount in the x-direction: "; cin>> x; cout<< "Enter the translation amount in the y-direction: "; cin>> y; cout<< endl; label = label + "Translation"; MtrxresultMtrx(x,y); returnresultMtrx; } } } // The retrieveVector function queries the user regarding a // 2D point, which is converted into a Vector and returned. VctrretrieveVector() { floatx, y; cout<< "Specify the x-coordinate of the point you plan to transform: "; cin>> x; cout<< "Specify the y-coordinate of the point you plan to transform: "; cin>> y; cout<< endl; returnVctr(x,y); } CS 240 87
CS 240 88
Templates Swap Function for double Swap Function for queue C++ provides a mechanism for avoiding redundancy in the creation of classes and functions. Swap Function for char Linked List Class for int Swap Function for string Linked List Class for matrix Linked List Class for nodePtr Swap Function for matrix Linked List Class for char Linked List Class for bool Swap Function for int Linked List Class for float Swap Function for stack Linked List Class for double Linked List Class for queue Swap Function for float Linked List Class for bankAcct Linked List Class for stack Swap Function for bool Linked List Class for string Linked List Class for long int CS 240 89
topGap indent1 midGap height indent2 Class Template Example: Two-Field Label The two-field label class template will allow two values, field1 and field2, to be placed into a label that can be output. field1 It is assumed that the classes of field1 and field2 both have a member function getLineNumber that takes an integer parameter and returns the corresponding line of the field, as well as a member function height that returns the number of output lines required by the field. field2 CS 240 90
Definition & Implementation of Two-Field Label Class // Member functions E1 getField1() const{return field1;} E2 getField2() const{return field2;} intgetIndent1() const{return indent1;} intgetIndent2() const{return indent2;} intgetHeight() const{return height;} intgetTopGap() const{returntopGap;} intgetMidGap() const{returnmidGap;} voidsetField1(const E1 &fd1) {field1 = fd1;} voidsetField2(const E2 &fd2) {field2 = fd2;} voidsetIndent1(int in1) {indent1 = (in1 < 0) ? 0 : in1;} voidsetIndent2(int in2) {indent2 = (in2 < 0) ? 0 : in2;} voidsetHeight(int h) {height = (h < 0) ? 0 : h;} voidsetTopGap(inttg) {topGap = (tg < 0) ? 0 : tg;} voidsetMidGap(int mg) {midGap = (mg < 0) ? 0 : mg;} template<typename E1, typenameE2> friendostream& operator << (ostream &os, consttwoFieldLabel<E1, E2> &tfl); private: // Data members E1 field1; E2 field2; intindent1; intindent2; intheight; inttopGap; intmidGap; }; //////////////////////////////////////////////////// // Class definition file: TwoFieldLabel.h // // // // This file defines the two-field label class of // // objects. Each two-field label contains two // // template-based fields that will be displayed // // in a rectangular region. This region will // // have a specified width and height, as well as // // an indentation for each field, a vertical gap // // between the fields, and a gap between the top // // of the rectangular region and the first field. // // An output operator is also specified for the // // two-field label. // //////////////////////////////////////////////////// #ifndefTWO_FIELD_LABEL_H #include <iostream> using namespace std; constintMIN_LABEL_HEIGHT = 3; /*** DEFINITION SECTION ***/ template <class E1, class E2> classtwoFieldLabel { public: // Constructors twoFieldLabel(); twoFieldLabel(intin1, intin2, inttg, intmg); twoFieldLabel(inth); twoFieldLabel(E1 f1, E2 f2); twoFieldLabel(consttwoFieldLabel<E1, E2> &tfl); CS 240 91
/*** IMPLEMENTATION SECTION ***/ // Default constructor: Sets *this to // // have minimal height, gap sizes, and // // indentations, and no field values. // template <class E1, class E2> twoFieldLabel<E1, E2>::twoFieldLabel() { height = MIN_LABEL_HEIGHT; indent1 = indent2 = topGap = 0; midGap= 1; } // Initializing constructor: Sets *this to // // have the parameterized gap sizes and // // indentations, no field values, and // // minimal height to accommodate gap sizes. // template <class E1, class E2> twoFieldLabel<E1, E2>::twoFieldLabel(intin1, intin2, inttg, intmg) { indent1 = (in1 < 0) ? 0 : in1; indent2 = (in2 < 0) ? 0 : in2; topGap= (tg < 0) ? 0 : tg; midGap= (mg < 1) ? 1 : mg; height = topGap + midGap + 2; } // Initializing constructor: Sets *this to // // have the parameterized gap sizes and // // indentations, no field values, and // // minimal height to accommodate gap sizes. // template <class E1, class E2> twoFieldLabel<E1, E2>::twoFieldLabel(int h) { height = h; indent1 = indent2 = topGap = 0; midGap = 1; height = (h < MIN_LABEL_HEIGHT) ? MIN_LABEL_HEIGHT : h; } // Initializing constructor: Sets *this to // // have the parameterized field values, // // minimal height to accommodate the fields, // // and minimal gap sizes and indentation. // template <class E1, class E2> twoFieldLabel<E1, E2>::twoFieldLabel(E1 f1, E2 f2) { height = f1.height() + f2.height() + 1; indent1 = indent2 = topGap = 0; midGap= 1; field1 = f1; field2 = f2; } // Copy constructor: copies the field // // values, gap sizes, indentations and // // heights from the parameterized // // twoFaceLabelinto *this. // template <class E1, class E2> twoFieldLabel<E1, E2>::twoFieldLabel (consttwoFieldLabel<E1, E2> &tfl) { height = tfl.getHeight(); indent1 = tfl.getIndent1(); indent2 = tfl.getIndent2(); topGap= tfl.getTopGap(); midGap= tfl.getMidGap(); field1 = tfl.getField1(); field2 = tfl.getField2(); } CS 240 92
// The output operator is overloaded to // // appropriately format the two-field label. // template <class E1, class E2> ostream& operator << (ostream &os, const twoFieldLabel<E1, E2> &tfl) { inti, j; intbottomGap = tfl.getHeight() - tfl.getTopGap() - tfl.getField1().height() - tfl.getMidGap() - tfl.getField2().height(); for(i = 1; i <= tfl.getTopGap(); i++) os<< endl; for(i = 1; i <= tfl.getField1().height(); i++) { for(j = 1; j <= tfl.getIndent1(); j++) os<< ' '; os<< tfl.getField1().getLineNumber(i) << endl; } for(i = 1; i <= tfl.getMidGap(); i++) os<< endl; for(i = 1; i <= tfl.getField2().height(); i++) { for(j = 1; j <= tfl.getIndent2(); j++) os<< ' '; os<< tfl.getField2().getLineNumber(i) << endl; } for(i = 1; i <= bottomGap; i++) os<< endl; returnos; } #define TWO_FIELD_LABEL_H #endif CS 240 93
New Person Class For Use With The Two-Field Label Class /////////////////////////////////////////////////////////////// // Class definition file: Person.h // // This program file contains the definitions of the members // // of the person class, which consists of three strings // // representing a person's name and address information. // /////////////////////////////////////////////////////////////// #ifndefPERSON_H #include <string> using namespace std; constintNBR_OF_OUTPUT_LINES = 3; class person { public: // Constructors person(); person(constperson &p); person(string fnm, string ad1, string ad2); // Member functions intheight(); string getLineNumber(int i); protected: // Data members string fullName; string firstAddressLine; string secondAddressLine; }; #define PERSON_H #endif CS 240 94
///////////////////////////////////////////// // Class implementation file: Person.cpp // // // // This program file contains the actual // // implementations of the member functions // // of the person class defined in the // // Person.hheader file. // ///////////////////////////////////////////// #include "Person.h" #include <string> using namespace std; // The default constructor sets the three // // data members of *this to empty strings. // person::person() { fullName= firstAddressLine= secondAddressLine= ""; } // The copy constructor sets the three data // // members of *this to the corresponding data // // member values of the parameterized person. // person::person(const person &p) { fullName= p.fullName; firstAddressLine= p.firstAddressLine; secondAddressLine= p.secondAddressLine; } // The initializing constructor sets // // the three data members of *this to // // the parameterized string values. // person::person(string fnm, string ad1, string ad2) { fullName= fnm; firstAddressLine= ad1; secondAddressLine= ad2; } // The height member function returns // // the number of output lines that are // // needed to output the person *this. // intperson::height() { returnNBR_OF_OUTPUT_LINES; } // The getLineNumber member function returns // // the appropriate addressing line, depending // // upon the value of the integer parameter. // string person::getLineNumber(int i) { switch(i) { case3: returnsecondAddressLine; break; case2: returnfirstAddressLine; break; default: returnfullName; break; } } CS 240 95
Driver Using A Two-Field Label With Two Person Fields ///////////////////////////////////////////////// // Program file: MailingLabelDriver.cpp // // // // This driver program retrieves destination // // addresses for several customers from an // // external file, and then generates an output // // file of mailing labels for these items, // // using a user-supplied source address and // // the retrieved destination addresses. // ///////////////////////////////////////////////// #include "Person.h" #include "TwoFieldLabel.h" #include <iostream> #include <string> #include <fstream> using namespace std; constintMAX_STRING_SIZE = 40; constintMAX_FILENAME_SIZE = 80; constintRTRN_ADDR_INDENT = 2; constintDEST_ADDR_INDENT = 25; constintTOP_GAP = 2; constintMIDDLE_GAP = 8; constintLABEL_HEIGHT = 20; voidqueryUser(person &p, charinputFileName[], charoutputFileName[]); voidcreateLabels(person source, ifstream&personFile, ofstream &labelFile); twoFieldLabel<person, person> generateLabel(constperson &source, constperson &destination); // The main function queries the user for // // source & destination info, and generates // // the output file of mailing labels. // void main() { person source; charpersonFileName[MAX_FILENAME_SIZE]; charlabelFileName[MAX_FILENAME_SIZE]; ifstreampersonFile; ofstreamlabelFile; queryUser(source, personFileName, labelFileName); personFile.open(personFileName); labelFile.open(labelFileName); createLabels(source, personFile, labelFile); personFile.close(); labelFile.close(); return; } CS 240 96
// Function queryUser queries the user for the // // source person info, the name of the input file // // containing the data regarding each destination // // person, and the name of the output file for // // the labels that are being generated. // voidqueryUser(person &p, charinputFileName[], charoutputFileName[]) { charname[MAX_STRING_SIZE]; charaddr[MAX_STRING_SIZE]; charsite[MAX_STRING_SIZE]; cout<< "Enter the full name of the sender: "; cin.getline(name, MAX_STRING_SIZE); while(string(name) == "") cin.getline(name, MAX_STRING_SIZE); cout<< "Enter the street address of the sender: "; cin.getline(addr, MAX_STRING_SIZE); while(string(addr) == "") cin.getline(addr, MAX_STRING_SIZE); cout<< "Enter the city, state, and zip code " << "of the sender: "; cin.getline(site, MAX_STRING_SIZE); while(string(site) == "") cin.getline(site, MAX_STRING_SIZE); p = person(name, addr, site); cout<< "Enter the name of the file where the " << "destination addresses reside: "; cin>> inputFileName; cout<< "Enter the name of the file where the " << "mailing labels should be placed: "; cin>> outputFileName; cout<< endl << endl; } CS 240 97
// The createLabels function reads all of the person data from the // // personFileinput file, and generates a mailing label to each // // person, with the source parameter as the return address. Each // // label is then output to the labelFile output file. // voidcreateLabels(person source, ifstream &personFile, ofstream&labelFile) { twoFieldLabel<person, person> label; person destination; charname[MAX_STRING_SIZE]; charaddr[MAX_STRING_SIZE]; charsite[MAX_STRING_SIZE]; personFile.getline(name, MAX_STRING_SIZE); while((string(name) == "") && !personFile.eof()) personFile.getline(name, MAX_STRING_SIZE); while(!personFile.eof()) { personFile.getline(addr, MAX_STRING_SIZE); while(string(addr) == "") personFile.getline(addr, MAX_STRING_SIZE); personFile.getline(site, MAX_STRING_SIZE); while(string(site) == "") personFile.getline(site, MAX_STRING_SIZE); destination = person(name, addr, site); label = generateLabel(source, destination); labelFile<< label << endl; for(int i = 1; i <= DEST_ADDR_INDENT + MAX_STRING_SIZE; i++) labelFile<< '-'; personFile.getline(name, MAX_STRING_SIZE); while((string(name) == "") && !personFile.eof()) personFile.getline(name, MAX_STRING_SIZE); } } CS 240 98
Darth Vader 666 Imperial Lane Coruscant, CA 90210 Fred Flintstone One Million BC Lane Bedrock CA 00000 ----------------------------------------------------------------- Darth Vader 666 Imperial Lane Coruscant, CA 90210 Marge Simpson 417 Evergreen Street Springfield IL 36963 ----------------------------------------------------------------- Darth Vader 666 Imperial Lane Coruscant, CA 90210 ... Fred Flintstone One Million BC Lane Bedrock CA 00000 Marge Simpson 417 Evergreen Street Springfield IL 36963 RenHoek 1237 Stimpy Avenue Mudskipper WI 86420 Scooby Doo 666 Shaggy Drive Amityville MA 75421 ... // Function generateLabel produces the two-field label that will serve as a mailing label. // twoFieldLabel<person, person> generateLabel(constperson &source, constperson &destination) { twoFieldLabel<person, person> label(RTRN_ADDR_INDENT, DEST_ADDR_INDENT, TOP_GAP, MIDDLE_GAP); label.setHeight(LABEL_HEIGHT); label.setField1(source); label.setField2(destination); returnlabel; } LBL.txt addresses.txt CS 240 99
New LabelString Class For Use With The Two-Field Label Class //////////////////////////////////////////////// // Class definition file: LabelString.h // // // // This program file contains the definitions // // of the members of the labelString class, // // which consists of a single string value. // //////////////////////////////////////////////// #ifndefLABEL_STRING_H #include <string> using namespace std; classlabelString { public: // Constructors labelString(); labelString(constcharstr[]); labelString(conststring &str); labelString(constlabelString &ls); // Member functions intheight(); string getLineNumber(int i); protected: // Data member string text; }; #define LABEL_STRING_H #endif CS 240 100
//////////////////////////////////////////////// // Class implementation file: LabelString.cpp // // // // This program file contains the actual // // implementations of the member functions // // of the labelStringclass defined in the // // LabelString.hheader file. // //////////////////////////////////////////////// #include <string> #include "LabelString.h" using namespace std; // The copy constructor sets the // // labelString's text data member // // to an empty string. // labelString::labelString() { text = ""; } // This initializing constructor sets // // the labelString'stext data member // // to the parameterized character array. // labelString::labelString(constcharstr[]) { text = str; } // This initializing constructor sets // // the labelString'stext data member // // to the parameterized string value. // labelString::labelString(const string &str) { text = str; } // The copy constructor sets the *this // // label String's text data member to // // the parameterized labelString's text // // data member. // labelString::labelString(constlabelString &ls) { text = ls.text; } // The height member function returns the // // number of output lines that are needed // // to output the labelString *this. // intlabelString::height() { return1; } // The getLineNumber member function // // disregards the integer parameter // // and merely retrieves the text data // // member of the *this labelString. // string labelString::getLineNumber(int i) { returntext; } CS 240 101
Driver Using A Two-Field Label With One Person Field And One LabelString Field // The main function opens the I/O files, re- // // trieves the employee data from the input file, // // and generates the output file of labels. // void main() { charemployeeFileName[MAX_FILENAME_SIZE]; charlabelFileName[MAX_FILENAME_SIZE]; ifstreamemployeeFile; ofstreamlabelFile; queryUser(employeeFileName, labelFileName); employeeFile.open(employeeFileName); labelFile.open(labelFileName); generateLabelFile(employeeFile, labelFile); employeeFile.close(); labelFile.close(); return; } // Function queryUser queries the user for // // the names of the input file containing // // the employee data and the output file // // for the labels being generated. // voidqueryUser(charinputFileName[], charoutputFileName[]) { cout<< "Enter the name of the file containing" << " the employee information: "; cin>> inputFileName; cout<< "Enter the name of the file where the" << " personnel folder labels should go: "; cin>> outputFileName; cout<< endl << endl; } //////////////////////////////////////////////// // Program file: PersonnelFileDriver.cpp // // // // This driver program retrieves personnel // // information (SSN, name, department number, // // mailstop) from an external file, and then // // generates an output file of folder labels // // for these employees, alternating between // // three different indentations. // //////////////////////////////////////////////// #include "Person.h" #include "TwoFieldLabel.h" #include "LabelString.h" #include <iostream> #include <string> #include <fstream> using namespace std; constintMAX_STRING_SIZE = 20; constintMAX_FILENAME_SIZE = 40; constintNBR_LABEL_STYLES = 4; voidqueryUser(charinputFileName[], charoutputFileName[]); voidgenerateLabelFile(ifstream &employeeFile, ofstream&labelFile); twoFieldLabel<labelString, person> generateLabel(constlabelString &ssn, constperson &employee); CS 240 102