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
*/