2-7) Templates


C++ provides templates to implement Generic Programming.

Template provide a mechanism the allows the programmers to maintain one version of source code, while the compiler, at compile time, creates multiple versions of the code appropriate for the intended types.

Having only one version of the source code is extremely helpful in keeping bugs from propagating because of multiple, slightly different, versions of code.

Also, the compiler also creates only those versions of the code that are actually needed.

Template are resolved at compile time so they do not incur a performance penalty like, say, virtual functions might.

The two examples below show how the code might look before and after the use of templates.



Example 1:  Code Before Templates
 

Example 1 Sample Code


Notice how each version of the set: setOfPerson, setOfBankAccount, setOfInt only vary in the type they old (made clear with the typedef before each class). This duplication of code is bad.

 

//person.h
class Person{
private:
   string name;
public:
   Person(
const string & aName): name(aName) {
   }
   Person(
const Person & p): name(p.name) {
   }
   Person &
operator=(const Person & p){
      cout <<
"Person:: operator=()\n";
      name = p.name;
     
return *this;
   }
  
void printOn(ostream & o) const { o << name; }
   };
 

   ostream & operator<<(ostream & o, const Person & p){
      p.printOn(o);
     
return
o;
   }


//bankaccount.h
class BankAccount{
private:
   Person owner;
  
double balance;
public:
   BankAccount(
const Person & theOwner, double openingBalance): owner(theOwner), balance(openingBalance){
   }

   BankAccount(
const BankAccount & b): owner(b.owner), balance(b.balance) {
   }
   BankAccount &
operator=(const BankAccount & b){
      cout <<
"BankAccount::operator=()\n";
      owner = b.owner;
      balance = b.balance;
     
return *this;
   }

  
void printOn(ostream & o) const { o << owner << " $" << balance; }
   };
  

   ostream & operator<<(ostream & o, const BankAccount & b){
      b.printOn(o);
     
return o;
}


//setOfPerson.h

typedef Person T_Person;
class SetOfPerson{
private: T_Person ** elements;
  
int size;
  
int capacity;
public:
   SetOfPerson(
int aCapacity = CAPACITY) : size(0), capacity(aCapacity), elements(new T_Person*[aCapacity]){
   }
  
bool isEmpty()const {return size <= 0;}
  
bool isFull() const {return size >= capacity;}
  
void add(T_Person & anElement) {
      
if( !isFull()) elements[size++] = &anElement;
     
 else{ cout << "WARNING: Set is full, item not added\n";}
    }
   T_Person & removeLast(){
     
if(isEmpty()){
         cout <<
"ERROR: Set is empty\n";
        exit(-1);
      }
     
else
          return *elements[--size];
      }
  
void printOn(ostream & o) const{
      o <<
"Set:\n";
     
if(isEmpty()) o << "EMPTY";
     
else{
         
for(int i = 0; i<size; i++) o << "\t" << *elements[i] << "\n"
;
      }
   }

};

ostream & operator<<(ostream & o, const SetOfPerson & s){
      s.printOn(o);
     
return
o;
}

//setOfBankAccount.h

typedef BankAccount T_BankAccount;
class SetOfBankAccount{
private: T_BankAccount** elements;
  
int size;
  
int capacity;
public:
   SetOfBankAccount(
int aCapacity = CAPACITY) : size(0), capacity(aCapacity), elements(new T_BankAccount*[aCapacity]){
   }
  
bool isEmpty()const {return size <= 0;}
  
bool isFull() const {return size >= capacity;}
  
void add(T_BankAccount& anElement) {
      
if( !isFull()) elements[size++] = &anElement;
     
 else{ cout << "WARNING: Set is full, item not added\n";}
    }
   T_BankAccount& removeLast(){
     
if(isEmpty()){
         cout <<
"ERROR: Set is empty\n";
        exit(-1);
      }
     
else
          return *elements[--size];
      }
  
void printOn(ostream & o) const{
      o <<
"Set:\n";
     
if(isEmpty()) o << "EMPTY";
     
else{
         
for(int i = 0; i<size; i++) o << "\t" << *elements[i] << "\n"
;
      }
   }

};

ostream & operator<<(ostream & o, const SetOfBankAccount & s){
      s.printOn(o);
     
return
o;
}

//setOfInt.h

typedef int T_int;
class SetOfInt{
private: T_int** elements;
  
int size;
  
int capacity;
public:
   SetOfInt(
int aCapacity = CAPACITY) : size(0), capacity(aCapacity), elements(new T_int*[aCapacity]){
   }
  
bool isEmpty()const {return size <= 0;}
  
bool isFull() const {return size >= capacity;}
  
void add(T_int& anElement) {
      
if( !isFull()) elements[size++] = &anElement;
     
 else{ cout << "WARNING: Set is full, item not added\n";}
    }
   T_int& removeLast(){
     
if(isEmpty()){
         cout <<
"ERROR: Set is empty\n";
        exit(-1);
      }
     
else
          return *elements[--size];
      }
  
void printOn(ostream & o) const{
      o <<
"Set:\n";
     
if(isEmpty()) o << "EMPTY";
     
else{
         
for(int i = 0; i<size; i++) o << "\t" << *elements[i] << "\n"
;
      }
   }

};

ostream & operator<<(ostream & o, const SetOfInt& s){
      s.printOn(o);
     
return
o;
}




//main.cpp
#include
<iostream> //for ostream
#include <string> //for string type

using namespace std;

const
int CAPACITY = 100;

#include
"person.h"
#include "bankaccount.h"
#include "setOfPerson.h"
#include "setOfBankAccount.h"
#include
"setOfInt.h"

void main(){
   Person p1(
"Lou");
   Person p2(
"Sue");
   cout << p1 <<
"\n";
   cout << p2 <<
"\n";

   SetOfPerson people(3);
   people.add(p1);
   people.add(p2);
   cout << people;

   BankAccount b1(p1, 0.0);
   BankAccount b2(p2, 100.0);
   cout << b1 <<
"\n";
   cout << b2 <<
"\n";
   b1 = b2;

   SetOfBankAccount accounts(10);
   accounts.add(b1);
   accounts.add(b2);
   cout << accounts;

  
int x = 45;
  
int y = 10;
  
int z = 12;

   SetOfInt someInts;
   someInts.add(x);
   someInts.add(y);
   someInts.add(z);
   cout << someInts;
 

}
 

/*output

Lou
Sue
Set:
   Lou
   Sue
Lou $0
Sue $100
BankAccount::operator=()
Person:: operator=()
Set:
   Sue $100
   Sue $100
Set:
   45
   10
   12
*/



Example 2: Code With Templates
 
Notice with templates their is only one version of the set source code, and the compiler will generate the actual classes: Set<Person>, Set<BankAccount>, and Set<int> as it discovers, in program main(), that those versions of the sets are needed.

This is much preferred to duplicate code, or containers the hold "objects" whose elements must be re-cast when they are removed from the container.

Example 2 Sample Code

//set.h
 

const int CAPACITY = 100;

template <class T>
class Set{
private: T ** elements;
  
int size;
  
int capacity;
public:
   Set(
int aCapacity = CAPACITY) : size(0), capacity(aCapacity), elements(new T*[aCapacity]){
   }
  
bool isEmpty()const {return size <= 0;}
  
bool isFull() const {return size >= capacity;}
  
void add(T & anElement) {
      
if( !isFull()) elements[size++] = &anElement;
     
 else{ cout << "WARNING: Set is full, item not added\n";}
    }
   T & removeLast(){
     
if(isEmpty()){
         cout <<
"ERROR: Set is empty\n";
        exit(-1);
      }
     
else
          return *elements[--size];
      }
  
void printOn(ostream & o) const{
      o <<
"Set:\n";
     
if(isEmpty()) o << "EMPTY";
     
else{
         
for(int i = 0; i<size; i++) o << "\t" << *elements[i] << "\n"
;
      }
   }

};

template <class T>
ostream &
operator<<(ostream & o, const Set<T> & s){
      s.printOn(o);
     
return
o;
}


//main.cpp
#include
<iostream> //for ostream
#include <string> //for string type

using namespace std;


#include
"person.h"
#include "bankaccount.h"
#include
"set.h"

void main(){
   Person p1(
"Lou");
   Person p2(
"Sue");
   cout << p1 <<
"\n";
   cout << p2 <<
"\n";

   Set<Person> people(3);
   people.add(p1);
   people.add(p2);
   cout << people;

   BankAccount b1(p1, 0.0);
   BankAccount b2(p2, 100.0);
   cout << b1 <<
"\n";
   cout << b2 <<
"\n";
   b1 = b2;

   Set<BankAccount> accounts(10);
   accounts.add(b1);
   accounts.add(b2);
   cout << accounts;

  
int x = 45;
  
int y = 10;
  
int z = 12;

   Set<int> someInts;
   someInts.add(x);
   someInts.add(y);
   someInts.add(z);
   cout << someInts;
 

}
 


/*output

Lou
Sue
Set:
   Lou
   Sue
Lou $0
Sue $100
BankAccount::operator=()
Person:: operator=()
Set:
   Sue $100
   Sue $100
Set:
   45
   10
   12
*/