[ fromfile: templates.xml id: classtemplates ]
Like functions, classes can also use parameterized types. Class templates are used to generate generic containers of data. The parameter is the answer to the question, “Container of what?“ All Qt container classes and, of course, all classes in the Standard Template Library (STL) are parameterized. Figure 10.1 shows a UML diagram of two template classes.
UML locates the template parameter in a small offset box in the upper right corner of the class box. Example 10.3 contains definitions for these classes.
Example 10.3. src/containers/stack/stack.h
[ . . . . ]
#include <QDebug>
template<class T> class Node {
public:
Node(T invalue): m_Value(invalue), m_Next(0) {}
~Node() ;
T getValue() const {return m_Value;}
void setValue(T value) {m_Value = value;}
Node<T>* getNext() const {return m_Next;}
void setNext(Node<T>* next) {m_Next = next;}
private:
T m_Value;
Node<T>* m_Next;
};
template<class T> Node<T>::~Node() {
qDebug() << m_Value << " deleted " << endl;
if(m_Next) {
delete m_Next;
}
}
template<class T> class Stack {
public:
Stack(): m_Head(0), m_Count(0) {}
~Stack<T>() {delete m_Head;} ;
void push(const T& t);
T pop();
T top() const;
int count() const;
private:
Node<T> *m_Head;
int m_Count;
};
Notice that template definitions, shown in Example 10.4 (classes and functions) appear in the header file.
This is necessary for the compiler to generate code from a template declaration.
Also notice the required template declaration code: template<class T>.
This code must precede each class or function definition that has a template parameter in its name.
Example 10.4. src/containers/stack/stack.h
[ . . . . ]
template <class T> void Stack<T>::push(const T& value) {
Node<T> *newNode = new Node<T>(value);
newNode->setNext(m_Head);
m_Head = newNode;
++m_Count;
}
template <class T> T Stack<T>::pop() {
Node<T> *popped = m_Head;
if (m_Head != 0) {
m_Head = m_Head->getNext();
T retval = popped->getValue();
popped->setNext(0);
delete popped;
--m_Count;
return retval;
}
return 0;
}
The creation of objects is handled generically in the template function, push().
The destructor for the Node<T> class recursively deletes Node pointers until it reaches one with a zero m_Next pointer[32].
Controlling creation and destruction of Node<T> objects this way enables Stack<T> to completely manage its dynamic memory.
Example 10.5 contains some client code to demonstrate these classes.
Example 10.5. src/containers/stack/main.cpp
#include <QDebug> #include <QString> #include "stack.h" int main() { Stack<int> intstack1, intstack2; int val; for (val = 0; val < 4; ++val) { intstack1.push(val); intstack2.push(2 * val); } while (intstack1.count()) { val = intstack1.pop(); qDebug() << val; } Stack<QString> stringstack; stringstack.push("First on"); stringstack.push("second on"); stringstack.push("first off"); QString val2; while (stringstack.count()) { val2 = stringstack.pop(); qDebug() << val2; } qDebug() << "Now intstack2 will self destruct."; return 0; }
When we run the program, we should see the output below.
3 deleted 3 2 deleted 2 1 deleted 1 0 deleted 0 first off deleted "first off" second on deleted "second on" First on deleted "First on" Now intstack2 will self destruct. 6 deleted 4 deleted 2 deleted 0 deleted
[32] This is a consequence of the fact that calling delete on a pointer automatically invokes the destructor associated with that pointer.
| Generated: $Date: 2009-09-08 12:15:32 -0400 (Tue, 08 Sep 2009) $ | © 2009 Alan Ezust and Paul Ezust. |