Software Reuse with C++ Template Class

 

 

Overview

 

Inheritance and containment are good, but they aren't always the answer to a desire to reuse code.

For example, we defined a function that swapped two int values. Something likes:
void swap(int &a, int &b);

If we want to swap two double value instead of int, then, one approach is to duplicate the original code, but replace each int with double

Now, if we need to swap two chars, then, we have to duplicate again. At the end of the day, we have three set of similar code to maintain and change ...

C++'s function template capability automates the process, saving you time and providing greater reliability

 

Function Template Example

 

// Format example
template <class Any>	// Set up template, and name it arbitray type Any
			// FYI - A lot of people like just a name T
void swap(Any &a, Any &b)	// Swap logic
{
	Any temp;
	temp = a;
	a = b;
	b = temp;
};

If we want a function to swap int, then the compiler will create a function following the template pattern, substituting int for Any. Similarly, if you need a function to swap doubles, the compiler will follow the template, substituting the double type for Any

Use templates if you need functions that apply the same algorithm to a variety of types

Many C++ compilers do not yet support templates at the time of this writing. Also, until the final version of the ANSI/ISO C++ standard comes out (when ?), template details are subject to change.

// Example:
// funtemp.cpp -- using a function template
#include <iostream.h>
// function template prototype
template <class Any>
void swap(Any &a, Any &b);

int main(void)
{
	int i = 10, j = 20;
	cout << "i, j = " << i << ", " << j << ".\n";
	cout << "Using compiler-generated int swapper:\n";
	swap(i,j);	// generates void swap(int &, int &)
	cout << "Now i, j = " << i << ", " << j << ".\n";
	
	double x = 24.5, y = 81.7;
	cout << "x, y = " << x << ", " << y << ".\n";
	cout << "Using compiler-generated double swapper:\n";
	swap(x,y);	// generates void swap(double &, double &)
	cout << "Now x, y = " << x << ", " << y << ".\n";
	
	return 0;
}

// function prototype definition
template <class Any>
void swap(Any &a, Any &b)
{
	Any temp;
	temp = a;
	a = b;
	b = temp;
};

 

Function Template - Specializations

 

If you want to use the same kind of function to do the function call, instead of using a compiler generated template function. Then, a specification will be useful

// Old Form Example
// Depends on what version of compiler you use, we might have old form
// or new form
template <class Any>
void swap(Any &a, Any &b);	// template prototype
void swap(int &n, int &m);	// regular prototype - specification
int main(void)
{
	double u, v;
	...
	swap(u, v);		// use template
	int a, b;
	...
	swap(a, b);		// use void swap(int &, int &)
}

The original template facility called for the compiler to use the nontemplate version, treating it as a specialization of the template

// New Form Example
// Some says old form is bad, because if you include some header files
// from other people, you might not have full control over which function
// will get call
// We use the new form to do:
template <class Any>
void swap(Any &a, Any &b);		// template prototype
void swap<int>(int &n, int &m);		// specialization prototype
int main(void)
{
	double u, v;
	...
	swap(u, v);		// use template
	int a, b;
	...
	swap(a, b);		// use void swap<int>(int &, int &)
}

Note that the <int> appears in the prototype, not in the function call. It also should appear in the function definition

 

Class Templates

 

The concept of function templates can be pushed one level higher to become class templates

Suppose we have an ArrayDB class that will store integers and have methods to deal with them ... now, it will be nice if we could use the same code to store double, float or char or string

C++'s class templates provide a good way to generate generic class declarations

Different level of compilers implement different level of tempalte support

Templates provide parameterized types, that is, the capability of passing a type name as an argument to an algorithm for building a class or a function. By feeding the type name int to a ArrayDB template, for example, you can get the compiler to construct a ArrayDB class for dealing with ints

 

Class Template Example

 

The following example shows the combined class and member function templates

It is important to realize that these are not class and member function definitions, they are instructions to the C++ compiler about how to generate class and member functions

A particular actualization of a template, such as a stack class for handling String objects, is called an instantiation

If you separate the template member functions in a separate implementation file, most compilers will not compile

The simplest way to make this work is to place all the template information in a header file and to include the header file in the file that will use the templates

Example:

// stacktp.h
#include "booly.h"

template <class Type>
class Stack
{
private:
	enum {MAX = 10};	// constant specific to class
	Type items[MAX];	// holds stack items
	int top;		// index for top stack item
public:
	Stack();
	Bool isempty();
	Bool isfull();
	Bool push(const Type & item);	// add item to stack
	Bool pop(Type & item);	// pop top into item
};

template <class Type>
Stack<Type>::Stack()
{
	top = 0;
}

template <class Type>
Bool Stack<Type>::isempty()
{
	return top == 0? True: False;
}

template <class Type>
Bool Stack<Type>::isfull()
{
	return top == MAX? True :False;
}

template <class Type>
Bool Stack<Type>::push(const Type & item)
{
	if (top < MAX)
	{
		items[top++] = item;
		return True;
	}
	else
		return False;
}

template <class Type>
Bool Stack<Type>::pop(Type & item)
{
	if (top > 0)
	{
		item = items[--top];
		return True;
	}
	else
		return False;
}

 

Using a Template Class - Example

 

// stacktem.cpp -- test template stack class
// compiler with strng2.cpp
#include <iostream.h>
#include <ctype.h>
#include "stacktp.h"
#include "strng2.h"
int main(void)
{
	Stack<String> st;	// create an empty stack
	char c;
	String po;
	cout << "Please enter A to add a purchase order,\n"
		 << "P to process a PO, or Q to quit.\n";
	while (cin >> c && toupper(c) != 'Q')
	{
		while (cin.get() != '\n')
			continue;
		if (!isalpha(c))
		{
			cout << '\a';
			continue;
		}
		switch(c)
		{
			case 'A':
			case 'a':	cout << "Enter a PO number to add: ";
						cin >> po;
						if (st.isfull())
							cout << "stack already full\n";
						else
							st.push(po);
						break;
			case 'P':
			case 'p':	if (st.isempty())
							cout << "stack already empty\n";
						else {
							st.pop(po);
							cout << "PO #" << po << " popped\n";
							break;
						}
		}
		cout << "Please enter A to add a purchase order,\n"
			 << "P to process a PO, or Q to quit.\n";
	}
	cout << "Bye\n";
	return 0;
}

 

 

Lab

 

Node.hpp (class definition and implementation)

LinkedList.hpp (template class definition)

Shape.hpp (modified version from Week 8)

week9.cpp (source code)

week9.out (output)