Appendix A
Quick reference for pclasses
These class definitions are copied from the pclasses web page, http://www.ibiblio.org/obp/pclasses/, with a few minor formatting changes.
pstring
template <class T>
class pstringT
{
public:
    pstringT<T>();                              
    pstringT<T>(const pstringT<T> &);           
    pstringT<T>(const T *copy);                 
    pstringT<T>(T ch);                          
    virtual ~pstringT<T>();                     
    inline T * c_str() const;                   
    inline int length() const;                  
    int find(const pstringT<T> &str) const;     
    int find(const T ch) const;                 
    pstringT<T> substr(int pos, int len) const; 
    T & operator [] (int n);                    
    const T operator [] (int n) const;          
    const pstringT<T> & operator = (const pstringT<T> &);   
    const pstringT<T> & operator = (const T * const);       
    const pstringT<T> & operator = (const T);               
    const pstringT<T> & operator += (const pstringT<T> &);  
    const pstringT<T> & operator += (const T * const);      
    const pstringT<T> & operator += (const T);              
protected:
    T *mystring;
};
typedef pstringT<char> pstring;
template <class T> pstringT<T> operator + (const pstringT<T> &, const pstringT<T> &);
template <class T> pstringT<T> operator + (const pstringT<T> &, T);
template <class T> pstringT<T> operator + (T, const pstringT<T> &);
template <class T> pstringT<T> operator + (const pstringT<T> &, const T * const);
template <class T> pstringT<T> operator + (const T * const, const pstringT<T> &);
template <class T> inline ostream & operator << (ostream &, const pstringT<T> &);
template <class T> istream & operator >> (istream &, pstringT<T> &);
template <class T> istream & getline(istream &, pstringT<T> &);
template <class T> inline bool operator == (const pstringT<T> &, const pstringT<T> &);
template <class T> inline bool operator != (const pstringT<T> &, const pstringT<T> &);
template <class T> inline bool operator <  (const pstringT<T> &, const pstringT<T> &);
template <class T> inline bool operator <= (const pstringT<T> &, const pstringT<T> &);
template <class T> inline bool operator >  (const pstringT<T> &, const pstringT<T> &);
template <class T> inline bool operator >= (const pstringT<T> &, const pstringT<T> &);
template <class T>
ostream & operator << (ostream &os, const pstringT<T> &out)
{
    return os << out.c_str() << flush;
}
template <class T>
istream & operator >> (istream &is, pstringT<T> &in)
{
    fflush(stdin);
    T input_buffer[4096];
    is >> input_buffer;
    in = input_buffer;
    return is;
}
template <class T>
istream & getline(istream &is, pstringT<T> &to_get)
{
    fflush(stdin);
    T getline_buffer[4096];
    getline(is, getline_buffer, 4095);
    to_get = getline_buffer;
    return is;
}
template <class T>
pstringT<T>::pstringT()
{
    mystring = new T[1];
    mystring[0] = 0;
}
template <class T>
pstringT<T>::pstringT(const T *copy)
{
    mystring = new T[strlen(copy) + 1]; 
    strcpy(mystring, copy);                 
}
template <class T>
pstringT<T>::pstringT(T ch)
{
    mystring = new T[2];
    mystring[0] = ch;
    mystring[1] = 0;
}
template <class T>
pstringT<T>::pstringT(const pstringT<T> & to_create_from)
{
    mystring = new T[to_create_from.length()+1];
    strcpy(mystring, to_create_from.c_str());   
}
template <class T>
pstringT<T>::~pstringT<T>()
{
    delete[] mystring;
}
template <class T>
T* pstringT<T>::c_str() const
{
    return mystring;
}
template <class T>
int pstringT<T>::length() const
{
    return strlen(mystring);
}
template <class T>
int pstringT<T>::find(const pstringT<T> & str) const
{
    int i, j, endsearch = length() - str.length() + 1;
    for(i = 0; i < endsearch; i++)
    {
        for(; i < endsearch && mystring[i] != str[0]; i++);
        if(i == endsearch)
            break;
        for(j = 0; j < str.length() && mystring[i+j] == str[j]; j++);
        if(j == str.length())
            return i;
    }
    return -1;
}
template <class T>
int pstringT<T>::find(const T ch) const
{
    for(int i = 0; i < length(); i++)
        if(mystring[i] == ch)
            return i;
    return -1;
}
template <class T>
pstringT<T> pstringT<T>::substr(int pos, int len) const
{
    if(pos < 0 || len < 0 || pos >= length())
    {
        cerr << "\nError: substring (" << pos << "," << len
             << ") out of bounds for string \"" << mystring << '\"' << endl;
        exit(1);
    }
    if(pos + len > length())
        len = length() - pos;
    T *result = new T[len + 1];
    memcpy(result, mystring + pos, len * sizeof(T));
    result[len] = 0;
    pstringT<T> to_return(result);
    delete[] result;
    return to_return;
}
template <class T>
const pstringT<T> & pstringT<T>::operator = (const T * const to_copy)
{
    delete[] mystring;                  
    mystring = new T[strlen(to_copy)+1];
    strcpy(mystring, to_copy);          
    return *this;
}
template <class T>
const pstringT<T> & pstringT<T>::operator = (const T ch)
{
    delete[] mystring;                  
    mystring = new T[2];
    mystring[0] = ch;
    mystring[1] = 0;
    return *this;
}
template <class T>
const pstringT<T> & pstringT<T>::operator = (const pstringT<T> ©)
{
    return *this = copy.c_str();        
}
template <class T>
const pstringT<T> & pstringT<T>::operator += (const T * const to_append)
{
    T *newbuffer = new T[length() + strlen(to_append) + 1];
    strcpy(newbuffer, mystring);
    strcat(newbuffer, to_append);
    delete[] mystring;
    mystring = newbuffer;
    return *this;
}
template <class T>
const pstringT<T> & pstringT<T>::operator += (const pstringT<T> &to_append)
{
    return *this += to_append.c_str();  
}
template <class T>
const pstringT<T> & pstringT<T>::operator += (const T to_append)
{
    T *newstring = new T[length()+2];
    strcpy(newstring, mystring);
    delete[] mystring;
    mystring = newstring;               
    mystring[length()+1] = 0;       
    mystring[length()] = to_append;
    return *this;
}
template <class T>
pstringT<T> operator + (const pstringT<T> & lval, const pstringT<T> & rval)
{
    pstringT<T> to_return(lval);
    return to_return += rval;
}
template <class T>
pstringT<T> operator + (const pstringT<T> & lval, const T & rval)
{
    pstringT<T> to_return(lval);
    return to_return += rval;
}
template <class T>
pstringT<T> operator + (T lval, const pstringT<T> & rval)
{
    pstringT<T> to_return(lval);
    return to_return += rval;
}
template <class T>
pstringT<T> operator + (const pstringT<T> & lval, const T * const rval)
{
    pstringT<T> to_return(lval);
    return to_return += rval;
}
template <class T>
pstringT<T> operator + (const T * const lval, const pstringT<T> & rval)
{
    pstringT<T> to_return(lval);
    return to_return += rval;
}
template <class T>
T & pstringT<T>::operator [] (int n)
{
    if(n<0 || n>=length())
    {
        cerr << "\nError: index out of range: " << n << " in string \""
             << mystring << "\" of length " << length() << endl;
        exit(1);
    }
    return mystring[n];
}
template <class T>
const T pstringT<T>::operator [] (int n) const
{
    if(n<0 || n>=length())
    {
        cerr << "\nError: index out of range: " << n << " in string \""
             << mystring << "\" of length " << length() << endl;
        exit(1);
    }
    return mystring[n];
}
template <class T>
bool operator == (const pstringT<T> &lval, const pstringT<T> &rval)
{
    return strcmp(lval.c_str(), rval.c_str()) == 0;
}
template <class T>
bool operator != (const pstringT<T> &lval, const pstringT<T> &rval)
{
    return strcmp(lval.c_str(),rval.c_str()) != 0;
}
template <class T>
bool operator < (const pstringT<T> &lval, const pstringT<T> &rval)
{
    return strcmp(lval.c_str(), rval.c_str()) < 0;
}
template <class T>
bool operator <= (const pstringT<T> &lval, const pstringT<T> &rval)
{
    return strcmp(lval.c_str(), rval.c_str()) <= 0;
}
template <class T>
bool operator > (const pstringT<T> &lval, const pstringT<T> &rval)
{
    return strcmp(lval.c_str(), rval.c_str()) > 0;
}
template <class T>
bool operator >= (const pstringT<T> &lval, const pstringT<T> &rval)
{
    return strcmp(lval.c_str(), rval.c_str()) >= 0;
}
pvector
template <class T>
class pvector
{
public:
    pvector();                                  
    pvector(int size);                          
    pvector(int size, const T &fill_val);       
    pvector(const pvector<T> &);                
    virtual ~pvector();                         
    void resize(int new_size);                  
    inline int length() const;                  
    T & operator [] (int index);                
    const T & operator [] (int index) const;    
    const pvector<T> & operator = (const pvector<T> &); 
protected:
    T *array;
    int len;
};
template <class T>
pvector<T>::pvector() :array(0), len(0)
{}
template <class T>
pvector<T>::pvector(int size)
{
    if(size <= 0)
    {
        cerr << "\nError: invalid pvector dimension: " << size << endl;
        exit(1);
    }
    array = new T[size];
    len = size;
}
template <class T>
pvector<T>::pvector(int size, const T &fill_val)
{
    array = new T[size];
    len = size;
    for(int i=0; i<size; i++)
        array[i] = fill_val;
}
template <class T>
pvector<T>::pvector(const pvector<T> &vec)
{
    array = new T[vec.length()];
    for(int i=0; i<vec.length(); i++)
        array[i] = vec[i];
    len = vec.length();
}
template <class T>
pvector<T>::~pvector()
{
    delete[] array;
}
template <class T>
int pvector<T>::length() const
{
    return len;
}
template <class T>
T & pvector<T>::operator [] (int index)
{
    if(index < 0 || index >= length())
    {
        cerr << "\nError: index out of range: " << index
             << " in pvector of length " << length() << endl;
        exit(1);
    }
    return array[index];
}
template <class T>
const T & pvector<T>::operator [] (int index) const
{
    if(index < 0 || index >= length())
    {
        cerr << "\nError: index out of range: " << index
             << " in pvector of length " << length() << endl;
        exit(1);
    }
    return array[index];
}
template <class T>
const pvector<T> & pvector<T>::operator = (const pvector<T> & vec)
{
    delete[] array;
    array = new T[vec.length()];
    for(int i=0; i<vec.length(); i++)
        array[i] = vec[i];
    len = vec.length();
    return *this;
}
template <class T>
void pvector<T>::resize(int new_size)
{
    if(new_size <= 0)
    {
        cerr << "\nError: invalid pvector dimension: " << new_size << endl;
        exit(1);
    }
    T *newarray = new T[new_size];
    int minsize = (new_size<len)?new_size:len;
    for(int i=0; i<minsize; i++)
        newarray[i] = array[i];
    delete[] array;
    array = newarray;
    len = new_size;
}
pmatrix
template <class T>
class pmatrix
{
public:
    pmatrix();                                          
    pmatrix(int rows, int cols);                        
    pmatrix(int rows, int cols, const T & fillvalue);   
    pmatrix(const pmatrix<T> &);                        
    virtual ~pmatrix();                                 
    void resize(int rows, int cols);                    
    inline int numrows() const;                         
    inline int numcols() const;                         
    inline pvector<T> & operator [] (int index);            
    inline const pvector<T> & operator [] (int index) const;
    const pmatrix<T> & operator = (const pmatrix<T> &);     
protected:
    pvector< pvector<T> > matrix;
};
template <class T>
pmatrix<T>::pmatrix()
{}
template <class T>
pmatrix<T>::pmatrix(int rows, int cols)
{
    resize(rows,cols);
}
template <class T>
pmatrix<T>::pmatrix(int rows, int cols, const T & fillvalue)
{
    resize(rows,cols);
    for(int x = 0; x < rows; x++)
        for(int y = 0; y < cols; y++)
            matrix[x][y] = fillvalue;
}
template <class T>
pmatrix<T>::pmatrix(const pmatrix<T> & copy)
{
    *this = copy;
}
template <class T>
pmatrix<T>::~pmatrix()
{}
template <class T>
int pmatrix<T>::numrows() const
{
    return matrix.length();
}
template <class T>
int pmatrix<T>::numcols() const
{
    return (matrix.length())?matrix[0].length():0;
}
template <class T>
void pmatrix<T>::resize(int rows, int cols)
{
    matrix.resize(rows);
    for(int x = 0; x < rows; x++)
        matrix[x].resize(cols); 
}
template <class T>
const pmatrix<T> & pmatrix<T>::operator = (const pmatrix<T> & copy)
{
    matrix = copy.matrix;       
    return *this;
}
template <class T>
const pvector<T> & pmatrix<T>::operator [] (int index) const
{
    return matrix[index];
}
template <class T>
pvector<T> & pmatrix<T>::operator [] (int index)
{
    return matrix[index];
}
pstack
template <class T>
class pstack
{
public:
    pstack();                                   
    pstack(const pstack<T> &);                  
    virtual ~pstack();                          
    void push(T storage);                       
    void pop(T & storage);                      
    const T pop();                              
    const T top() const;                        
    void makeEmpty();                           
    inline bool isEmpty() const;                
    inline int length() const;                  
    const pstack<T> & operator = (const pstack<T> &);   
protected:
    struct node
    {
        T data;
        node *next;
        node() :next(0) {}
        node(const T & a) :next(0), data(a) {}
    } *sp;
    int size;
};
template <class T>
pstack<T>::pstack() :sp(0), size(0)
{}
template <class T>
pstack<T>::pstack(const pstack<T> ©) :sp(0), size(0)
{
    *this = copy;
}
template <class T>
pstack<T>::~pstack()
{
    makeEmpty();
}
template <class T>
void pstack<T>::push(T storage)
{
    
    node *newnode = new node;
    newnode->next=sp;
    newnode->data=storage;
    sp = newnode;
    ++size;
}
template <class T>
void pstack<T>::pop(T & storage)
{
    if(isEmpty())
    {
        cerr << "\nError: accessing empty stack through method pstack::pop\n";
        exit(1);
    }
    
    storage = sp->data;
    
    node *temp = sp;
    sp = sp->next;
    delete temp;
    --size;
}
template <class T>
const T pstack<T>::pop()
{
    T val;
    pop(val);
    return val;
}
template <class T>
const T pstack<T>::top() const
{
    if(isEmpty())
    {
        cerr << "\nError: accessing empty stack through method pstack::top\n";
        exit(1);
    }
    return sp->data;
}
template <class T>
int pstack<T>::length() const
{
    return size;
}
template <class T>
void pstack<T>::makeEmpty()
{
    node *temp;
    
    while(sp!=0)
    {
        temp = sp;
        sp = sp->next;
        delete temp;
    }
    size = 0;
}
template <class T>
bool pstack<T>::isEmpty() const
{
    return size == 0;
}
template <class T>
const pstack<T> & pstack<T>::operator = (const pstack<T> & copy)
{
    makeEmpty();
    if(copy.isEmpty())
        return *this;
    sp = new node(copy.sp->data);
    node *newnode,*end = sp;
    for(newnode = copy.sp->next; newnode; newnode = newnode->next)
    {
        end->next = new node(newnode->data);
        end = end->next;
    }
    size = copy.size;
    return *this;
}
pqueue
template <class T>
class pqueue
{
public:
    pqueue();                               
    pqueue(const pqueue<T> &);              
    virtual ~pqueue();                      
    void enqueue(const T &data);            
    void dequeue(T &storage);               
    const T dequeue();                      
    const T & front() const;                
    void makeEmpty();                       
    inline bool isEmpty() const;            
    inline int length() const;              
    const pqueue<T> & operator = (const pqueue<T> &);   
protected:
    struct node
    {
        T data;
        node *next;
        node() :next(0) {}
        node(const T &a) :next(0), data(a) {}
    } *head, *tail;
    int size;
};
template <class T>
pqueue<T>::pqueue() :head(0), tail(0), size(0)
{}
template <class T>
pqueue<T>::pqueue(const pqueue<T> ©) :head(0), tail(0), size(0)
{
    *this = copy;
}
template <class T>
pqueue<T>::~pqueue()
{
    makeEmpty();
}
template <class T>
void pqueue<T>::enqueue(const T &data)
{
    node *newnode = new node(data); 
    if(size==0) 
    {
        head = newnode;
        tail = head;
    }
    else
    {
        tail->next = newnode;
        tail = newnode;
    }
    ++size;
}
template <class T>
void pqueue<T>::dequeue(T &storage)
{
    if(head==0)
    {
        cerr << "\nError: accessing empty queue through method pqueue::dequeue\n";
        exit(1);
    }
    storage = head->data;               
    node *temp = head;                  
    head = head->next;                  
    delete temp;                        
    --size;                             
}
template <class T>
const T pqueue<T>::dequeue()
{
    T val;              
    dequeue(val);       
    return val;
}
template <class T>
const T & pqueue<T>::front() const
{
    if(head==0)
    {
        cerr << "\nError: accessing empty queue through method pqueue::front\n";
        exit(1);
    }
    return head->data;
}
template <class T>
void pqueue<T>::makeEmpty()
{
    node *temp;
    while(head)
    {
        temp = head;
        head = head->next;
        delete temp;
    }
    size = 0;
}
template <class T>
const pqueue<T> & pqueue<T>::operator = (const pqueue<T> & copy)
{
    makeEmpty();
    if(copy.isEmpty())
        return *this;
    head = new node(copy.head->data);
    tail = head;
    node *newnode;
    for(newnode = copy.head->next; newnode; newnode = newnode->next)
    {
        tail->next = new node(newnode->data);
        tail = tail->next;
    }
    size = copy.size;
    return *this;
}
template <class T>
int pqueue<T>::length() const
{
    return size;
}
template <class T>
bool pqueue<T>::isEmpty() const
{
    return size == 0;
}