Week 9 Lecture Summary for CSC 309

These notes are not intended to be complete. They serve as an outline to the class and as a supplement to the text.


Code Reuse


Class Templates

Recall the dictionary class used a vector of pair objects to represent the mapping of english words to spanish words. That pair class mapped strings to strings. Suppose we wanted to map some other type T to another (possible different) type U. Using class templates we can write a prototype for the pair class (or any class) and let the compiler generate type specific classes.

Here is the definition for such a class.

template <typename T, typename U>
class Pair
{
    private:
        T key;
        U value;
    public:
        Pair(T k, U v = U()) :
            key(k), value(v) {}
        
        T getKey()    const {return key;}
        U getValue()  const {return value;}        
        void setValue(U );   
       
};
template <typename T, typename U>
void Pair<T,U>::setValue(U v) {
    value = v;
}
Pair<int,int>       p1(34,56);
Pair<char,int>      p2('A',65);
Pair<string,string> p3("hello", "hola");

Book b("Time", "Wells", "1-345-4567-X", "34.95")
Pair<string,book>   p4("1-345-4567-X", b);
template <typename T, typename U>
ostream& operator<< (ostream& out, const Pair<T,U> & p) {
    out << "(" << p.getKey() << ", " << p.getValue() << ")" ;
    return out;
}

Now we could execute the following piece of code.

cout << p1 << endl << p2 << endl << p3 << endl; 
bool lessThanK(const Pair<string,string> & p) {
    return p.getKey()[0] < 'K' ||
           p.getKey()[0] < 'k' ;
}

Composition

Composition- A form of aggregation in which a class has an instance variable of some class type. Objects built from these classes will have references to other objects. Composition models the HAS - A relationship.

We can say:

Consider the following classes that could be used in maintaining a phone directory.

name.h

#ifndef NAME_H
#define NAME_H

#include <string>
#include <iostream>
using namespace std;

class Name
{
    private:
        string first;
        string middle;
        string last;
    public:
        Name(string f, string m, string l) :
            first(f), middle(m), last(l) {}
        Name(string f, string l) :
            first(f), middle(""), last(l) {}
        Name():
            first(""), middle(""), last("") {}
        
        string getFirst()  const  {return first;}
        string getMiddle() const  {return middle;}
        string getLast()   const  {return last;}
        
        string toString()  const;
};

ostream& operator<<(ostream& out, const Name & n);
#endif

friend.h

#ifndef FRIEND_H
#define FRIEND_H

#include "name.h"
#include <string>
#include <iostream>

using namespace std;

class Friend
{
    private:
        Name name;
        string phone;
    public:
        Friend(Name n, string p) :
            name(n), phone(p) {}
        Friend(string first, string last, string p) :
            name(Name(first,last)), phone(p) {}
        Friend () {}
        
        Name  getName()   const {return name;}
        string getPhone() const {return phone;}
        string toString() const;
};

ostream& operator<<(ostream& out, const Friend & fr);
#endif

Inheritance

When designing a project, this feature allows you to arrange classes into a hierarchy where the higher-level classes group together the common properties and behaviors. Lower level classes can extend the higher level classes and do not need to repeat the common fields and behaviors.

For example the classes involved in a bank application used for customer accounts could be of type.

BankAccount, SavingsAccount, CheckingAccount ,MonyMarketAccount, CDAccount etc...

All accounts have a customer name, address, balance etc ..

Some of the accounts have a minimum balance.

Some accounts have a fee per check. etc...

Some accounts are timed

Here is one way to relate these classes.

BankAccount Definition

bankaccount.h

#ifndef BANKACCOUNT_H
#define BANKACCOUNT_H
#include <string>
#include <iostream>
using namespace std;

class BankAccount {        
        private:
            string name;            
        
        protected:
            double balance;           
            
        public:
           BankAccount(string n, double b) :
                name(n), balance(b) {}
           
           BankAccount () : 
                name(""), balance(0.0) {}
           
           string getName()    const {return name;}                         
           double getBalance() const {return balance;}
           
           void withDraw(double amount);
           void deposit(double amount);
};

#endif

checking.h

#ifndef CHECKING_H
#define CHECKING_H

#include "bankaccount.h"
#include <string>
using namespace std;

class CheckingAccount : public BankAccount
{
    private:
        double fee;
    public:
        CheckingAccount() :
            BankAccount(), fee(0.0) {}
        CheckingAccount (string n, double b, double f ) :
            BankAccount(n,b), fee(f) {}
        
        double getFee() const {return fee;}
        void withDraw(double amount);
};

#endif

More Features

  BankAccount      ba("tony", 1000);          // base class object  
  CheckingAccount  ca("kathy",1000, .05);     // subclass class object
  
  ba.getName();
  ba.deposit(1500);  
  ba.getBalance();
  
  
  ca.getName();
  ca.deposit(500); 
  ca.getBalance();
 
  • Both CheckingAccount and BankAccount define a withDraw method.
  • Only CheckingAccount defines a getFee method. This method can only be invoked on CheckingAccount objects or derived classes from CheckingAccount, not BankAccount.
  • ba.getFee()   // error
    ca.getFee()   // okay
    
    ba.withDraw(200);    // okay, calls withdraw from BankAccount class
    ca.withDraw(100);    // okay, calls withdraw from CheckingAccount class. 
    
  • Each private member in BankAccount (base class) is visible only in BankAccount. Subclasses are not allowed to directly access private members. For example the following constructor definition for CheckingAccount would be illegal.
  • CheckingAccount (string n, double b, double f ) :
    
             name(n), balance(b), fee(f) {}       // name is private in class BankAccount 
    
  • A private base class member is inherited by the derived class. That is, each CheckingAcount object has a name, balance and fee field. The name and balance field are inherited from BankAccount.
  • An object of a derived class has living inside it a base class object. When a derived class object is created the default constructor of the base class is invoked before the body of the derived constructor is executed. The base class constructor takes care of initializing the base class portion of the derived object.
  • To specify a different base class constructor use the name of the base class in the initializer list.
  • CheckingAccount (string n, double b, double f ) :
                BankAccount(n,b), fee(f) {}
    

    Method Implementations

    BankAccount

    void BankAccount::withDraw(double amount){    
        if (amount <= balance){
            balance -= amount;
        }else{
            cout  << "insufficient funds"  << endl;
        }
    }
    void BankAccount::deposit(double amount){
        if(amount >=0){
            balance += amount;
        }else{
            cout << " can't deposit a negative amount " << endl;
        }
    }
    

    CheckingAccount

    void CheckingAccount::withDraw(double amount){
        this -> BankAccount::withDraw(amount);
        balance -= fee;
    } 
    

    Polymorphic Variables

    void print(BankAccount acct) {
        cout << acct.getName() << " has a balance of ...  "
             << "$" << acct.getBalance() << endl;
    }
    bool operator<(BankAccount acct1, BankAccount acct2) {
        return acct1.getName() < acct2.getName();
    }
    
    print(ba);
    print(ca);
      
    cout << (ba < ca) << endl;
    cout << (ca < ba) << endl;
    
    ba = ca;  // Danger
    
    
    void print(const BankAccount &  acct) {
        cout << acct.getName() << " has a balance of ...  "
             <<  "$" <<  acct.getBalance()  <<  endl;
    }
    bool operator<(const BankAccount & acct1, const BankAccount & acct2) {
        return acct1.getName()  < acct2.getName();
    }
    

    Visibility Rules