16.4. Class Template Members
The QueueItemClass
We'll start our implementation by writing the QueueItemclass:
template <class Type> class QueueItem {
// private class: no public section
QueueItem(const Type &t): item(t), next(0) { }
Type item; // value stored in this element
QueueItem *next; // pointer to next element in the Queue
};
Each time we instantiate a Queueclass, the same version of QueueItemwill be instantiated as well.
For example, if we create Queue<int>, then a companion class, QueueItem<int>, will be
instantiated.
Class QueueItemis a private classit has no public interface. We intend this class to be used to implement Queueand have not built it for general use. Hence, it has no public members. We'll need to make class Queuea friend of QueueItemso that its members can access the members of QueueItem.
The Queue Class
We can now flesh out our Queueclass:
template <class Type> class Queue {
public:
// empty Queue
Queue(): head(0), tail(0) { }
// copy control to manage pointers to QueueItems in the Queue
Queue(const Queue &Q): head(0), tail(0)
{ copy_elems(Q); }
Queue& operator=(const Queue&);
~Queue() { destroy(); }
// return element from head of Queue
// unchecked operation: front on an empty Queue is undefined
Type& front() { return head->item; }
const Type &front() const { return head->item; }
void push(const Type &); // add element to back of Queue
void pop (); // remove element from head of Queue
bool empty () const { // true if no elements in the Queue
return head == 0;
}
private:
QueueItem<Type> *head; // pointer to first element in Queue
QueueItem<Type> *tail; // pointer to last element in Queue
// utility functions used by copy constructor, assignment, and destructor
void destroy(); // delete all the elements
void copy_elems(const Queue&); // copy elements from parameter
};
References to a Template Type in the Scope of the Template
Ordinarily, when we use the name of a class template, we must specify the template parameters.
There is one exception to this rule: Inside the scope of the class itself, we may use the unqualified name of the class template. For example, in the declarations of the default and copy constructor the name Queueis a shorthand notation that stands for Queue<Type>. Essentially the compiler infers that when we refer to the name of the class, we are referring to the same version. Hence, the copy constructor definition is really equivalent to writing:
Queue<Type>(const Queue<Type> &Q): head(0), tail(0)
{ copy_elems(Q); }
The compiler performs no such inference for the template parameter(s) for other templates used within the class. Hence, we must specify the type parameter when declaring pointers to the companion QueueItemclass:
QueueItem<Type> *head; // pointer to first element in Queue
QueueItem<Type> *tail; // pointer to last element in Queue
These declarations say that for a given instantiation of class Queue, headand tailpoint to an object of type QueueIteminstantiated for the same template parameter. That is, the type of head and tailinside the Queue<int>instantiation is QueueItem<int>*. It would be an error to omit the template parameter in the definition of the headand tailmembers:
QueueItem *head; // error: which version of QueueItem?
QueueItem *tail; // error: which version of QueueItem?