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: Approach

Design a single class, List, that will handle the type 2 code for both WordNodes and LineNodes
Four list operations are required:
  1. Append a node to the end of the list.
  2. Remove a node from the list. This operation will be needed only in LSReset which removes all the list elements.
  3. Retrieve the ith node in the list.
  4. 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;
}