Object-oriented Programming and the KWIC system---Part 1
OOP benefits in KWIC
For many of the KWIC modules, OOP offers little potential for improvement.
The LineStorage module, however, is repetitive in a way
that has considerable potential.
Converting the LineStorage module to C++
Consider the current LineStorage implementation:
- It is not extremely complex.
Two kinds of linked lists are created:
the list of LineNodes and one WordNode list for each LineNode.
Both are conventional: singly-linked lists with tail pointers.
The list access is straightforward.
- Still, it is clumsy, tedious, error-prone,
because of the nearly duplicated code for LineNode and WordNode lists.
- LineStorage code is of two main types:
- Node allocation, initialization, and de-allocation.
- Linking, unlinking, and traversing links.
- For the lists of WordNodes and LineNodes:
the type 1 code is different but the type 2 code is almost identical.
Approach
Design a single class, List, that will handle the type 2 code
for both WordNodes and LineNodes
Four list operations are required:
- Append a node to the end of the list.
- Remove a node from the list.
This operation will be needed only in LSReset
which removes all the list elements.
- Retrieve the ith node in the list.
- Return the size of the list.
List.h
/********** List module---interface specification **********/
/***** overview *****
The List module provides a class template for a generic list service.
Objects of type Node (or a class derived from Node) may be added to the
end of the list, removed from the front, and located by integer
position. No calls to new or delete are made by List; these are the
responsibility of the List user.
*/
/***** constants *****/
/***** types *****/
struct Node {
Node* nextPtr;
Node() { nextPtr = 0; }
};
/***** local classes *****/
class Base {
public:
Base();
void add(Node*);
Node* remove();
Node* operator[](int);
int size();
private:
Node* headPtr;
Node* tailPtr;
int listSize;
};
/***** state invariant *****
1. headPtr points to a null-terminated list of Nodes
(or a class derived from Node).
2. if headPtr != 0 then tailPtr points to the last Node in this list.
3. There are listSize Nodes in this list.
*/
/***** exported classes *****/
template class List : private Base {
public:
// add a to the tail of the list
void add(T* a) { Base::add(a); }
// if listSize > 0 then
// remove the head Node and return its address
// else
// return 0
T* remove() { return (T*) Base::remove(); }
// if i is in [0..size()-1] then
// return a pointer to the i'th element of the list, zero-relative
// else
// return 0
T* operator[](int i) { return (T*) Base::operator[](i); }
// return the number of elements in the list
int size() { return Base::size(); }
};
List.C
#include <iostream.h>
#include "List.h"
Base::Base()
{
headPtr = 0;
listSize = 0;
}
void Base::add(Node* addPtr)
{
listSize++;
if (headPtr == 0) {
headPtr = addPtr;
} else {
tailPtr->nextPtr = addPtr;
}
tailPtr = addPtr;
addPtr->nextPtr = 0;
}
Node* Base::remove()
{
if (headPtr == 0)
return 0;
Node* tmpPtr = headPtr;
headPtr = headPtr->nextPtr;
listSize--;
return tmpPtr;
}
Node* Base::operator[](int i)
{
if (i < 0 || i >= listSize)
return 0;
if (i == listSize-1) // optimization for LSAddWord
return tailPtr;
for (Node* tmpPtr = headPtr; i-- > 0; tmpPtr = tmpPtr->nextPtr)
; // intentionally empty
return tmpPtr;
}
int Base::size()
{
return listSize;
}
Driver.C
#include <iostream.h>
#include "List.h"
class IntNode : public Node {
public:
IntNode(int x0) : x(x0) { cout << "IntNode(int)" << endl; }
~IntNode() { cout << "~IntNode()" << endl; }
int x;
};
int main()
{
int i;
IntNode* intNodePtr;
// create
List intList;
for (i = 0; i < 3; i++) {
intNodePtr = new IntNode(10*i);
intList.add(intNodePtr);
}
// display
for (i = 0; i < intList.size(); i++) {
cout << "intList[" << i << "]: " << intList[i]->x << endl;
cout << endl;
}
// destroy
while (intNodePtr = intList.remove()) {
delete intNodePtr;
}
return 0;
}