Previous Section Main Menu Next Section

CHAPTER 8

AN EVEN BETTER CONTRACT PROGRAM


Section VI: Member Functions for ListOfContracts Section I: Analyzing the Code Implications of an Improved Contract Program Section II:Instances as Array Elements Section III: The List: A Look at Data Representation
Section IV: Lists: Working with Encapsulation Section V: Class Declarations for Contract and ListOfContracts Section VII: Improving ListOfContracts Section VIII: Rewriting the Nonmember Functions

A. Viewing Data Members as a Structure
Before we look at the member functions for 'ListOfContracts, we need to understand the data structure employed here. By this is meant how the data members of the class are related to and support each other. Up to now, the data members for our classes have been pretty much independent from each other. They were simply unrelated properties. With 'ListOfContracts' this is no longer true.

We have already talked in general about the two data members for this class. The second one, 'listOfContracts' is an array. We know this from the code because it is declared to be of type 'ContractArray' - the type we declared at the top of the file. By the way, note that the class is named 'ListOfContracts' with an upper case 'L', while our array has the same name, except that we use a lower case 'l'. Some people think this is neat, others feel it just adds to the confusion. You decide for yourself in your own work.

The first data member, 'numContracts', is the counter we talked about above. When the list is empty, it has the value 0, when one element is in the list, it has the value 1, etc. Note the relationship between 'numContracts' and 'listOfContracts'. The data member 'numContracts' tells the system which elements of 'listOfContracts' hold actual contracts and which ones hold garbage.

These two data members make up a structure that we can use to represent a list. Such a structure is only useful, however, if there are also functions to manipulate it. Developing these is our next step.

B. OverView of the Member Functions - Declarations and Definitions
The declaration along with the design of the six member functions is based on the following:

The declarations are in the file ch8list1.h. We are now ready for the function definitions in the file 'ch8list1.cpp'.

// Class Definitions for ListOfContracts
// File: ch8list1.cpp

#include <iostream.h>
#include "ch8list1.h"

ListOfContracts :: ListOfContracts()
{
	numContracts = 0;
}


void ListOfContracts :: Add(Contract contract)
{
	if (numContracts < MAX_CONTRACTS)  )       // There is room for another contract
	{	int index = 0;
		bool found;
		found = false;
		int contractNum = contract.ProvideID();
		while ((!found) && (index < numContracts)) // keep searching until ID found									     // or no more contracts in list
		{	if (contractNum == listOfContracts[index].ProvideID())
			// Does the ID of the contract to be added match the ID of the
			// contract in this array element?

			{	found = true;
			}
			else
			{	index++;
			}
		}
		if (!found) // contract with this ID does not exist
		{
			listOfContracts[numContracts] = contract; // copy contract into list
			numContracts++;
		}
		else
		{	cout << "\nA contract with this ID already exists.\n";
		}
	}
	else
	{	cout << "There was no room to add the contract\n\n";
	}
}

void ListOfContracts :: Delete(int contractNum)
{
	int index = 0;
	bool found;
	found = false;
	while ((!found) && (index < numContracts)) // keep searching until ID found									// or no more contracts in list.
	{	if (contractNum == listOfContracts[index].ProvideID())
		{	found = true;
		}
		else
		{	index++;
		}
	}
	if (found) // the contract to be deleted exists so we move the last contract into the
                   //  location of the to be deleted contract and decrement numContracts

	{	listOfContracts[index] = listOfContracts[numContracts-1];
		numContracts--;
	}
        else
	{	cout << "Invalid Contract ID\n\n";
	}
}


bool ListOfContracts :: FindContract(int ID, Contract& contract)
{
	int index = 0;
	while (index < numContracts) 	// keep searching until ID found
						// or no more contracts in list.
	{	if (ID == listOfContracts[index].ProvideID())
		{	contract = listOfContracts[index];
			return true;

		}
		else
		{	index++;
		}
	}
	return false;  // contract ID not found in list
}

void ListOfContracts :: ReplaceContract (Contract contract)
{
	int index = 0;
	bool found;
	found = false;
	int ID = contract.ProvideID();  // Get ID of the contract so we can search for it in
                                   			     // in the list.
	while ((! found) && (index < numContracts)) // keep searching until ID found
				                    		      // or no more contracts in list.
	{	if (ID == listOfContracts[index].ProvideID())
		{	found = true;
		}
		else
		{	index++;
		}
	}
	if (found) // the contract to be replaced exists
	{	listOfContracts[index] = contract;
	}
	else
	{	cout << "\nContract to be replaced does not exist in the list.\n";
	}
}

void ListOfContracts :: DisplayAll()
{
	int contractID;
	int sqFootage;
	int numDesks;
	int numDays;
	double charge;

	cout << endl << endl;  // To set off the output
	for (int contract = 0; contract < numContracts; contract++)
	{
		contractID = listOfContracts[contract].ProvideID();
		sqFootage = listOfContracts[contract].ProvideSquareFootage();
		cout << "The square footage for contract " << contractID << " is: "
			  << sqFootage << endl;

		numDesks = listOfContracts[contract].ProvideNumberOfDesks();
		cout << "The number of desks for contract " << contractID << " is: "
			  << numDesks << endl;

		numDays = listOfContracts[contract].ProvideNumberOfDays();
		cout << "The number of days for contract " << contractID << " is: "
			  << numDays << endl;


		charge = listOfContracts[contract].ProvidePerWeekCharge();
		cout << "The per week charge for contract " << contractID <<"  is: "
			  << charge << endl;
		cout << endl << endl;
	}

}

C. The Constructor
First, take a look at the constructor. All that visibly happens when the constructor is called is that the count for the number of contracts is set to 0. However, because each instance of 'ListOfContracts', by declaration, contains an array of contracts, calling the constructor for 'ListOfContracts' implies the creation of a number of individual contract instances. This further implies that a constructor for 'Contract' must be called as many times as there are elements in the array. Since no property values are being passed for each contract, the constructor with no parameters (see section I. C above) is called and each instance will have zero as the value of all its properties. In other words, declaring an instance of class 'ListOfContracts', causes the system to set aside enough memory for an integer (numContracts and limit) and an array of Contracts. Then, the act of declaring an array of Contracts calls a Contract constructor over and over. Since each instance in the array is instantiated without any property values, the version of the constructor with no parameters is called. This version sets all property values to 0.

D. The 'Add' Member Function
We now move to the 'Add' member function and explore the role of the data member, 'numContracts'. This variable has been designed to act both as a count of the number of elements in the array and as an index into the array. When the first contract is added, it should be placed into the zero-th element of the array. Note that this is the value of 'numContracts' the first time 'Add' is called. Thus, the line of code:

listOfContracts[numContracts] = contract;

This causes the contract in the variable 'contract' to be copied to the memory location 'listOfCOntracts[0]'. After this, 'numContracts' is incremented to 1 and the next time an add takes place, the new contract will be placed in the memory location symbolized by ' 'listOfContracts[1]'.

(Be sure you understand that the values 0 and 1 used here refer to the values held by 'numContracts' at different times.)

Let's step back and go over this function from the top. The first step is to make sure that there is not already a contract in the list with the ID of the new contract. (Duplicates can't be allowed if we want to be able to delete a contract based on it's ID.) The line

int contractNum = contract.ProvideID();

gets the ID of the contract to be added and the while loop examines all the contracts to check for a duplicate. Here we see another use of a boolean variable. A search can end for two reasons:

  1. The item searched for has been found;
  2. There are no more items to search

Thus, there are two test conditions in the while loop. Note that 'found' is initially set to false to indicate that a match has not been found when the loop starts. One by one, the loop compares the contracts in the list (using 'index' as the subscript into the array of contracts) with the ID of the new contract (as stored in 'contractNum'). If a match occurs, found is set to true and this is used to stop the search loop. If there is no match, 'index' is incremented and the next element in the array is used in the comparison. Eventually, if some contract in the list has an ID equal to the ID in 'contractNum', the loop will stop, with the 'index' acting as an indicator of which element in the array had the match. If there is no match in the list, the loop will eventually stop when 'index' reaches 'numContracts'. In this case, 'found' is never set to true. Note that the code does NOT search the whole array. It only checks those array elements that actually hold contracts – as indicated by the value in 'numContracts'. (If the whole array were checked, the 'while' test would have used MAX_CONTRACTS instead of 'numContracts'.)

The second step is to insert the contract if no match was found. The line:

if (!found)

performs the check to make sure there was no match. If so, the contract is added to the list and the number of contracts is incremented. The rest of the code simply adds appropriate messages.

E. The 'Delete' Member Function
'Delete' is the same as 'Add' except, of course, we want to remove a contract. The code again does a search followed by a test. This time, however, the test is to see if a match was found and is written as:

if (found)

The test here is the opposite of the test in 'Add' because the action takes place only if a match exists - one cannot delete something if it is not there to be deleted.

The other main difference in the code is in the lines:

A contract is not so much deleted as written over. In this code it is written over by the last contract in the list. Since the list of contracts is not sorted and, therefore, there is no concern about the order of contracts, it makes complete sense to simply move the last contract into the deleted contracts spot. Of course, the last contract now exists in two elements of the array - where it was before the copy and in the location containing the contract to be deleted. However, once we decrement 'numContracts' by one, there is no problem because the original location for the contract being copied is now considered to contain garbage.

One might ask why we copy from element "numContracts -1". Remember, 'numContracts' indicates the next available spot in the array. Therefore, "numContracts -1" indicates the "last used" element of the array.

An example: Consider a list containing five contracts (C1 through C5) and a maximum capacity of 10 contracts. Figure 8.5 show how memory might look at this point.

If contract C2 is to be deleted from a list, the goal is to copy contract C5 into the spot containing C2. C5 is in element 4 and C2 is in element 1 -- remember C++ arrays count from 0. The search code should have found C2 in element one and therefore placed the value 1 in 'index'. The code:

will change the picture of memory as shown in figure 8.6.

Contract 'C2' no longer exists while contract 'C5' is in two places. However, 'numContracts' now has the value 4 so the second 'C5', in element 5 of the array, is no longer seen as part of the list. This is a subtle point, so consider it carefully and create your own examples to convince yourself that this works.

F. 'FindContract', ReplaceContract' and "DisplayAll'
The member function, 'FindContract', uses essentially the same search code. (Mmmm, do we have a pattern here?) It does, however, take advantage of the way the 'return' statement works to simplify the while loop test condition. In the 'Add' and 'Delete modules the variable 'found' was used as a signal to leave the loop. These functions needed to do something after the loop - depending on whether a contract was found or not found. 'FindContract' has no further responsibility and thus can return as soon as a contract match is found.

When a system encounters a 'return' statement it immediately leaves whatever function it is in. Thus, when the line:

return true;

is encountered in 'FindContract', the code leaves the loop in the process of leaving the whole function. (Notice that if a match is found, this function also places the matching contract into the parameter 'contract' and this contract is then also returned.) As long as a match is not found, the index variable is incremented and the loop continues. If the loop terminates for any other reason, than when a match is found, it can only be because all contracts in the list have been examined. This means that no matching ID was found in the list and the function returns false.

'ReplaceContract' is yet another example of the same search/action pattern. This time, the goal is to find the location of the contract to be replaced and then copy into that location the contract passed as a parameter. The 'else' part of the 'if' statement in the code for this function is mostly for debugging. Used properly, there should always be a match. However, one should always code defensively and consider the possibility of the impossible!

The last function, 'DisplayAll', is different but very simple. The code shows us why we decided to make this a member function. It is so easy to simply walk through the list using a 'for' loop to output the property values of all the contracts. Note that 'contract' is used here in the way 'index' was used above and that the loop tests for:

contract < numContracts;

instead of comparing ' contract' with MAX_CONTRACTS. The goal is to display all the contracts that actually exist, not the values in all possible contract locations.

Top of Section Main Menu Next Section