
TemplatesWe are used to writing functions to be as general purpose as possible. For example, a squaring function would probably be written to take and reutrn doubles so that we can use it for doubles AND integers. However, what if we wanted to create a class of rational numbers (see the link above)? Then we would need to write a new squaring function that took Rationals and returned them. As we add classes, we will need to add functions that vary ONLY in the types of arguments and return value. what we'd really like is a function that took whatever type we passed it and returned the same type. Templates in C++ let us do exactly that. We can set up functions to use a variable type like this:
template<class T>
T square (T input) {
return input * input;
}
The first line states that we are going to use a template for this
function, and that anywhere that "T" appears in the function, we should
replace it with the appropriate type. so, how do we indicate what the
proper type is? there are two answers. the long answer is this: if
your function can tell what T is by the arguments, you just call it
like normal. This is the case with our square function. If we pass in
a double, the function knows T is a double, and that it should return
a double. However, if the function can't determine T by the input,
you need to specify. You are welcome to specify anyway (it never
hurts). and we do it with some special syntax when we call the
function:
int main() {
Rational x(2,3);
square,<Rational>(x);
}
So when might we have a function where the return type is unclear from the arguments? Consider this function. It takes a string as an argument. That string is a prompt for the user to enter some value. The function then reads in the answer and returns it. It could return a double, an int, a string, a Rational, or anything. If we just call the function with a string as an argument, it has no way of telling what we actually want returned.
template So that is templatizing functions. Wouldn't it be nice if we could do that with classes too? Templatizing ClassesConsider for a moment a stack of integers (not general purpose, so no inheritance):
class IntNode
{
IntNode * next;
int mValue;
public:
Node(int inVal, IntNode * inNext)
{
mValue = inVal;
next = inNext;
}
int GetVal() {return mValue;}
IntNode * GetNext() {return next;}
~IntNode() {return;}
};
class IntStack
{
IntNode * top;
public:
IntStack() {top = NULL;}
bool Empty() { return top == NULL; }
void Push(int inVal)
{
top = new IntNode(inVal, top);
}
int Pop()
{
IntNode * oldTop = top;
top = oldTop->GetNext();
int theReturn = oldTop->GetVal();
delete oldTop;
return theReturn;
}
};
So this is a nice integer stack that is easy to use and cleans up
after itself. Now if we wanted to use it again, for something else,
we would just go in and change int everywhere to be what we wanted it
to be, say double or some class that we define. This is exactly what
templates do for us, just in a bit more organized manner. So how do
we do this wonderful thing? We use the template keyword to make the
class a templatized class:
template <class T>
#include <iostream>
using namespace std;
template <class T>
class Node{
private:
Node * next;
T mValue;
public:
Node(T inVal, Node<T> * inNext)
{ mValue = inVal;
next = inNext;
}
T GetVal() {return mValue;}
Node * GetNext() {return next;}
~Node() {return;}
};
template <class stack_type>
class Stack
{
private:
Node<stack_type> * top;
public:
Stack() {top = NULL;}
bool Empty() { return top == NULL; }
void Push(stack_type inVal)
{
top = new Node<stack_type>(inVal, top);
}
stack_type Pop()
{
if (Empty())
{
cout << "Stack underflow error." << endl;
}
Node<stack_type> * oldTop = top;
top = oldTop->GetNext();
stack_type theReturn =oldTop->GetVal();
delete oldTop;
return theReturn;
}
};
So now we have a clean, very nice general purpose stack. How do we
use it:
main()
{
Stack<int> myIntStack;
myIntStack.Push(7);
int x = myIntStack.Pop();
}
and we can now put whatever type of value we want in there! And the
best thing is that now if we ever make changes in our stack all the
other stacks that we have written can benifit from them, and we never
need to write another stack again!
A spec for vectors |