I. concepts (HR 3.1) A. introduction ----------------------------- ABSTRACTION Ch. 1, control functions Ch. 2, information hiding modules (static) Ch. 3, data abstraction classes ----------------------------- B. abstract data types (ADTs) ---------------------------- ABSTRACT DATA TYPES rational numbers abstract values: 1/2, 3/4, 2/3 operations: numr, denr, make-ratl polynomials abstract values: 3.0x^2 + 5.1x + 3.4 operations: degree, leading-coef, rest-of-poly input streams abstract values: operations: >>, .get, !, ... def: an *abstract data type* (ADT) is a type specified by: (1) a set of *abstract values*, and (2) specifications for the *operations* on the values ---------------------------- ----------------------------- WHY A MODULE IS NOT AN ADT An ADT allows clients to create many *instances* Table myTbl, myOtherTbl; A module hides data and functions, but: - there is only one in a program, - doesn't define a type of data. ----------------------------- II. classes in C++ (HR 3.2-3.4, DD 6, 7.1-4, 7.8) A. initial class example (compare HR pp. 148ff) -------------------------- CLASSES IMPLEMENT ADTs in C++ // Ratl.h class Ratl { // ABSTRACTLY: math. rationals public: Ratl(int n, int d); // PRE: d != 0 // MODIFIES: self // POST: self = (n/d) int numr() const; // POST: FCTVAL == numerator of self int denr() const; // POST: FCTVAL == denominator of self private: int num; int den; }; -------------------------- -------------------------- TYPICAL FILE STRUCTURE // Ratl.h class Ratl { // ABSTRACTLY: math. rationals public: Ratl(int n, int d); // PRE: d != 0 // MODIFIES: self // POST: self = (n/d) int numr() const; // POST: FCTVAL == numerator of self int denr() const; // POST: FCTVAL == denominator of self #include "Ratl.pri" }; // Ratl.pri private: int num; int den; // Ratl.C #include "Ratl.h" // CLASSINV: den > 0; int Ratl::numr() const { return num; } int Ratl::denr() const { return den; } Ratl::Ratl(int n, int d) { num = n; den = d; } -------------------------- ------------------------- CLIENT CODE // RatClient.C #include #include "Ratl.h" Ratl add(Ratl a, Ratl b) // POST: FCTVAL == a + b { return Ratl(a.numr() * b.denr() + b.numr() * b.denr(), a.denr() * b.denr()); } void printRatl(Ratl r) { cout << r.numr() << "/" << r.denr(); } int main() { Ratl aThird(1,3); Ratl six = Ratl(6,1); Ratl ans = add(aThird, six); cout << "ans is "; printRatl(ans); cout << endl; return 0; } ------------------------- ------------------------------- FOR YOU TO DO Implement the following #include "Ratl.h" Ratl multiply(Ratl a, Ratl b); // POST: FCTVAL == a * b void swap(Ratl & a, Ratl & b); // POST: a == b && b == a ------------------------------- B. classes in C++ (HR 3.2) 1. private and public keywords --------------------- private: AND public: IN C++ class Password { char passStr[11]; public: void GetPassword(); Boolean ValidUser(); }; class Password { private: char passStr[11]; public: void GetPassword(); Boolean ValidUser(); }; class Password { public: void GetPassword(); Boolean ValidUser(); private: char passStr[11]; }; --------------------- 2. members ---------------------- ACCOUNT CLASS // Account.h class Account { // ABSTRACTLY: some number of dollars public: void Open(); // function member // MODIFIES: self // POST: balance of self is zero // NOTE: this must be called first void Deposit(int cts); // PRE: Open was called && cts >= 0 // MODIFIES: self // POST: the new balance is // self + cts/100 dollars double Dollars() const; // PRE: Open was called // POST: FCTVAL is approximately // the balance of self. #include "Account.pri" }; -------------------------------- -------------------------------- CLASS MEMBERS AND VISIBILITY // AccountClient.C -- client code #include #include "Account.h" int main() { Account acct1; Account acct2; acct1.Open(); acct1.Deposit(500); cout << acct1.Dollars() << endl; acct2.Open(); acct2.Deposit(1700); cout << acct2.Dollars() << endl; return 0; } ---------------------- What does this print? 3. kinds of operations -------------------------- MANIPULATING CLASS INSTANCES Account acct1, acct2; Can do: acct2.Open(); acct2.Deposit(20); double d = acct2.Dollars(); acct1 = acct2; ---------------------------- ---------------------------- Cannot do: double m = acct1 + acct2; Cannot do as a client: int k = acct1.numCents; ------------------------------ what does this have to do with information hiding? 4. class scope ----------------------------- CLASS SCOPE Local to Account class: Open, Deposit, Dollars Thus: // doesn't call Account::Deposit Deposit(2000); Account acct1; acct1.Deposit(2000); // does! // doesn't call Account::Deposit OtherType foo; foo.Deposit(2000); ----------------------------- 5. const member functions ------------------------------------ CONST MEMBER FUNCTIONS // AccountClient2.C -- client code #include "Account.h" int main() { Account acct1; acct1.Open(); acct1.Deposit(600); const Account acctConstant = acct1; double d = acctConstant.Dollars(); //ok acctConstant.Deposit(500); // error! return 1; } // Account.h class Account { // ABSTRACTLY: some number of dollars public: // ... double Dollars() const; // PRE: Open was called // POST: FCTVAL is approximately // the balance of self. //... }; // Account.C #include "Account.h" double Account::Dollars() const { return numCents / 100.0; } ------------------------------------ C. specification and implementation ------------------------------------ IMPLEMENTATION OF Account AND CONCRETE SPECIFICATIONS // Account.pri private: int numCents; // ABSTRACTION MAP: dollar balance // is numCents / 100. // Account.C #include "Account.h" ------------------------------------ how would you implement Accout::Deposit? D. constructors (HR 3.3) why is that bad? ------------------------------- CONSTRUCTORS GUARANTEE INITIALIZATION // Account.h #ifndef Account_h #define Account_h 1 class Account { // ABSTRACTLY: some number of dollars public: Account(); // default constructor // MODIFIES: self // POST: balance of self is zero Account(int cts); // another constructor // MODIFIES: self // POST: balance of self is cts/100 void Deposit(int cts); // PRE: cts >= 0 // MODIFIES: self // POST: the new balance is // self + cts/100 dollars double Dollars() const; // POST: FCTVAL is approximately // the balance of self. #include "Account.pri" }; #endif ------------------------------- ---------------------------- USE OF CONSTRUCTORS // AccountClient.C -- client code #include #include "Account.h" int main() { Account acct1; cout << acct1.Dollars() << endl; acct1.Deposit(500); cout << acct1.Dollars() << endl; Account acct2(1700); cout << acct2.Dollars() << endl; return 0; } ---------------------------- could we declare acct1 as a const? acct2? -------------------------- IMPLEMENTING CONSTRUCTORS Account::Account(int cts) { numCents = cts; } FOR YOU TO DO Write the implementation of the default constructor. -------------------------- E. class invariants ------------------------------- CLASS INVARIANTS // Account.h #ifndef Account_h #define Account_h 1 class Account { // ABSTRACTLY: some number of dollars // CLASSINV: the balance is not negative public: // ... }; #endif // Account.C // CLASSINV: the numCents is not negative // ... ------------------------------- -------------------------------- WHAT AN INVARIANT MEANS // Account.h #ifndef Account_h #define Account_h 1 class Account { // ABSTRACTLY: some number of dollars public: Account(); // MODIFIES: self // POST: balance of self is zero // && the balance is not negative Account(int cts); // another constructor // MODIFIES: self // POST: balance of self is cts/100 // && the balance is not negative void Deposit(int cts); // PRE: cts >= 0 // && the balance is not negative // MODIFIES: self // POST: the new balance is // self + cts/100 dollars // && the balance is not negative double Dollars() const; // PRE: the balance is not negative // POST: FCTVAL is approximately // the balance of self. // && the balance is not negative #include "Account.pri" }; #endif -------------------------------- F. example (p. 126ff) ------------------------- FOR YOU TO DO In groups of 3 or 4, design a class RandGen that is like the rand module, and record your design with a header file, with comments. // RandGen.h class RandGen { public: #include "RandGen.pri" }; ------------------------- what would client code that printed a random number look like? what would be involved in implementing this? G. layered software (3.4) --------------------------- PROBLEM Design a type that tracks a person's name and their checking and savings accounts. --------------------------- --------------------------- PASSING ARGUMENTS TO CONSTRUCTORS OF DATA MEMBERS // TwoAccounts.pri private: char name[20]; Account savings; Account checking; // TwoAccounts.C #include "TwoAccounts.h" #include TwoAccounts::TwoAccounts(const char owr[], int sv_cts, int chk_cts) // PRE: owr is null-terminated // && length of owr <= 20 // MODIFIES: self // POST: name is copied from owr, // && savings == sv_cts/100 // && checking == chk_cts/100 : savings(sv_cts), checking(chk_cts) { strcpy(name, owr); } --------------------------- can you implement a default constructor for TwoAccounts? III. overloading in C++ (HR 3.5-3.6, DD 8) A. a problem with binary member functions --------------------------------- BINARY FUNCTION PROBLEMS Check whether two accounts have the same balance. // Account_equal.C #include "Account.h" #include "pretend_bool.h" bool Equal(Account a1, Account a2) // POST: FCTVAL is true iff // the balances of a1 and a2 are // exactly equal { } --------------------------------- what else can we do as a client? B. solutions (3.5) 1. use a member function ---------------------------------- USING A MEMBER FUNCTION // Account.h #ifndef Account_h #define Account_h 1 #include "pretend_bool.h" class Account { // ABSTRACTLY: some number of dollars // CLASSINV: the balance is not negative public: Account(); // default constructor // MODIFIES: self // POST: balance of self is zero // ... bool Equal(Account a2) const; // POST: FCTVAL is true iff // the balances of self and a2 are // exactly equal #include "Account.pri" }; #endif ----------------------------------- ----------------------------------- IMPLEMENTATION OF MEMBER FUNCTION // Account.C #include "Account.h" Account::Account() { numCents = 0; } //... bool Account::Equal(Account a2) const { } ----------------------------------- ----------------------------- CLIENT CODE WITH MEMBER FUNCTION // AccountClient.C #include #include "Account.h" int main() { Account acct1; acct1.Deposit(500); Account acct2(1700); if ( acct1.Equal(acct2) ) { cout << "they're equal\n"; } else { cout << "they're not\n"; } } ----------------------------- 2. use a friend function ------------------------------ PROBLEM How to get symmetric client code? And still have efficiency of private access to data members? FRIENDSHIP A class can grant private access to named functions and classes, such functions and classes are called *friends*. ------------------------------ ------------------------------- GRANTING FRIENDSHIP // Account.pri private: int numCents; // ABSTRACTION MAP: dollar balance // is numCents / 100. friend bool Equal(Account a1, Account a2); and take Equal out of Account.h... ------------------------------- ------------------------------ IMPLEMENTING Equal AS A FRIEND FUNCTION // Account_equal.C #include "Account.h" #include "pretend_bool.h" bool Equal(Account a1, Account a2) // POST: FCTVAL is true iff // the balances of a1 and a2 are // exactly equal { } -------------------------------- ----------------------------- CLIENT CODE WITH FRIEND FUNCTION // AccountClient.C #include #include "Account.h" #include "Account_equal.h" int main() { Account acct1; acct1.Deposit(500); Account acct2(1700); if ( Equal(acct1, acct2) ) { cout << "they're equal\n"; } else { cout << "they're not\n"; } } ----------------------------- C. overloading (3.6) ------------------------------- PROBLEM Want to write if ( acct1 == acct2 ) { //... } SOLUTION: OPERATOR OVERLOADING Change name from Equal to operator == Can also overload +, -, *, /, <<, >>, ... ------------------------------- 1. friend overload ------------------------------- GRANTING FRIENDSHIP TO OVERLOAD // Account.pri private: int numCents; // ABSTRACTION MAP: dollar balance // is numCents / 100. friend bool operator == (Account a1, Account a2); ------------------------------- ------------------------------ IMPLEMENTING == AS A FRIEND FUNCTION // Account_equal.C #include "Account.h" #include "pretend_bool.h" bool operator ==(Account a1, Account a2) // POST: FCTVAL is true iff // the balances of a1 and a2 are // exactly equal { return a1.numCents == a2.numCents; } -------------------------------- 2. using a member function ---------------------------------- USING A MEMBER FUNCTION // Account.h #ifndef Account_h #define Account_h 1 #include "pretend_bool.h" class Account { // ABSTRACTLY: some number of dollars // CLASSINV: the balance is not negative public: Account(); // default constructor // MODIFIES: self // POST: balance of self is zero // ... bool operator ==(Account a2) const; // POST: FCTVAL is true iff // the balances of self and a2 are // exactly equal #include "Account.pri" }; #endif ----------------------------------- ----------------------------------- IMPLEMENTATION OF MEMBER FUNCTION bool Account::operator == (Account a2) const { return numCents == a2.numCents; } CLIENT CODE if ( acct1 == acct2 ) { //... } ----------------------------------- D. overloading the "put to" (output) operator <<, see DD pp. 448-449 ------------------------------ OVERLOADING operator << Suppose we want to write the following: // AccountClient.C #include "Account.h" int main() { Account acct(300); cout << acct << endl; } ------------------------------- ------------------------------- // Account.h #ifndef Account_h #define Account_h 1 #include class Account { // ... }; extern ostream & operator << (ostream & out, const Account & a); // PRE: out is in a good state // MODIFIES: out // POST: a printed form of a // is added to out ------------------------------- ------------------------------- // Account.C #include "Account.h" #include "iomanip.h" // ... ostream & operator << (ostream & out, const Account & a) { out << "$"; out.setf(ios::showpoint); out.setf(ios::fixed, ios::floatfield); out.precision(2); out << a.Dollars(); return out; } ------------------------------- E. additional example (if time)