out");
class Int {
int i;
public:
Int(int ii = 0) : i(ii) {
out << ">" << i << endl;
Chapter 14: Templates & Container Classes
492
}
~Int() { out << "~" << i << endl; }
operator int() const { return i; }
friend ostream&
operator<<(ostream& os, const Int& x) {
return os << x.i;
}
};
int main() {
TStash<Int> intStash; // Instantiate for Int
for(int i = 0; i < 30; i++)
intStash.add(new Int(i));
TStashIter<Int> intIter(intStash);
intIter.forward(5);
for(int j = 0; j < 20; j++, intIter++)
intIter.remove(); // Default removal
for(int k = 0; k < intStash.count(); k++)
if(intStash[k]) // Remove() causes "holes"
out << *intStash[k] << endl;
ifstream file("TStashTest.cpp");
assure(file, "TStashTest.cpp");
// Instantiate for String:
TStash<string> stringStash;
string line;
while(getline(file, line))
stringStash.add(new string(line));
for(int u = 0; u < stringStash.count(); u++)
if(stringStash[u])
out << *stringStash[u] << endl;
TStashIter<string> it(stringStash);
int j = 25;
it.forward(j);
while(it) {
out << j++ << ": " << it->c_str() << endl;
it++;
}
} ///:~
In both cases an iterator is created and used to move through the container. Notice the
elegance produced by using these constructs: You aren’t assailed with the implementation
details of using an array. You tell the container and iterator objects what to do, not how. This makes the solution easier to conceptualize, to build, and to modify.
Chapter 14: Templates & Container Classes
493
stack as a template
The Stack class, last seen in Chapter XX, is also a container and is also best expressed as a template with an associated iterator. Here’s the new header file:
//: C16:TStack.h
// Stack using templates
#ifndef TSTACK_H
#define TSTACK_H
// Declaration required:
template<class T> class TStackIterator;
template<class T> class TStack {
struct Link {
T* data;
Link* next;
Link(T* dat, Link* nxt) {
data = dat;
next = nxt;
}
}* head;
int _owns;
public:
TStack(int own = 1) : head(0), _owns(own) {}
~TStack();
void push(T* dat) {
head = new Link(dat,head);
}
T* peek() const { return head->data; }
T* pop();
int owns() const { return _owns; }
void owns(int newownership) {
_owns = newownership;
}
friend class TStackIterator<T>;
};
template<class T> T* TStack<T>::pop() {
if(head == 0) return 0;
T* result = head->data;
Link* oldHead = head;
head = head->next;
Chapter 14: Templates & Container Classes
494
delete oldHead;
return result;
}
template<class T> TStack<T>::~TStack() {
Link* cursor = head;
while(head) {
cursor = cursor->next;
// Conditional cleanup of data:
if(_owns) delete head->data;
delete head;
head = cursor;
}
}
template<class T> class TStackIterator {
TStack<T>::Link* p;
public:
TStackIterator(const TStack<T>& tl)
: p(tl.head) {}
TStackIterator(const TStackIterator& tl)
: p(tl.p) {}
// operator++ returns boolean indicating end:
int operator++() {
if(p->next)
p = p->next;
else p = 0; // Indicates end of list
return int(p);
}
int operator++(int) { return operator++(); }
// Smart pointer:
T* operator->() const {
if(!p) return 0;
return p->data;
}
T* current() const {
if(!p) return 0;
return p->data;
}
// int conversion for conditional test:
operator int() const { return p ? 1 : 0; }
};
#endif // TSTACK_H ///:~
Chapter 14: Templates & Container Classes
495
You’ll also notice the class has been changed to support ownership, which works now because the class knows the exact type (or at least the base type, which will work assuming
virtual destructors are used). As with TStash, the default is for the container to destroy its objects but you can change this by either modifying the constructor argument or using the
owns( ) read/write member functions.
The iterator is very simple and very small – the size of a single pointer. When you create a